Object Relational Mapping guide - Kohana


Object Relational Mapping guide - Kohana

 

Object Relational Mapping guide with Kohana PHP Framework

Kohana PHP Object Relational Mapping guide Written by Sam Clark Version 1.4 (Draft) http://sam.clark.name Contents Introduction 3 Credits and contributions 3 What is this ORM thing anyway? 4 Creating an ORM class 5 Using Customer_Model with Customer_Controller 8 Extending the behaviour of Customer_Model 14 Creating ORM relationships 16 One to many relationship 16 Many to many relationship 18 ORM relationships summary 20 Using ORM relationships in your code 21 ?nd_related 21 add 21 remove 21 Appendix A - Full Files 22 customers.sql 22 customer_addresses.sql 22 addresses.sql 23 customers_addresses.sql 23 application/models/customer.php 24 application/models/customer_address.php 24 application/models/address.php 24 application/controllers/customer.php 25 Kohana PHP Object Relational Mapping guide 2 Introduction The impetus for writing this guide came from discovering there was no real documentation for my favourite feature within Kohana PHP - ORM. ORM delivers a robust data manipulation toolset that provides almost everything you could possibly need to write clean, fast code to do wonderful things. After reading through many of the posts within the Kohana PHP forum, plus reading an introduction to ORM on the Kohana PHP Development blog, I decided to try and ?nish the missing bits regarding creating relationships. This document originally started as a blog post but as it started to grow, I decided to create a structured document rather than a meandering post. Because of this, sometimes the language in this document is in the ?rst person, sometimes in the third and sometimes the person is missing altogether. For this I can only apologise and say,?it will get better?. This is currently only the ?rst draft and I will update in time - but this will be helped if you help me by telling me what works for you, especially what doesn?t and anything you think I missed out. If you have any comments, just post them on my blog under the?Kohana PHP - ORM guide? post (http://sam.clark.name/2008/03/31/orm-guide/). Hopefully this should help the guys working hard to develop Kohana PHP with some extra documentation in the future. I am a PHP developer based in South London. I have been using and waxing lyrical about Kohana PHP for the last four months on my blog (http://sam.clark.name), as well as on the geekUp mailing list. I was inspired to write this document after reading?Kohana?s ORM - a brief introduction?at http://learn.kohanaphp.com/2008/02/14/kohanas-orm-a-brief-introduction/. You can read more about ORM at http://doc.kohanaphp.com/libraries/orm . Credits and contributions Proof reading and error correction by phelz Further proof reading and error corrections by atomless and Louis Licence Kohana PHP Object Relational Mapping Guide by Sam Clark is licensed under a Creative Commons Attribution-Non-Commercial-Share Alike 2.0 UK: England & Wales License. Based on a work at learn.kohanaphp.com. Kohana PHP Object Relational Mapping guide 3 What is this ORM thing anyway? ORM (Object Relational Mapping) is an object that mirrors data within a database. The idea of an Object Relational Mapping is to allow manipulation and control of data within a DB as though it was a PHP object. You may be thinking that this is just a database abstraction layer and you wouldn?t be entirely wrong. But ORM goes further than just removing SQL code from your PHP code, it allows you to tailor how data moves to and from di?erent tables within your Database. Using ORM within your code allows you to pull data from your database, manipulate the data in any way you like and then save the result back to the database without one line of SQL. It goes further by providing methods for maintaining relationships across tables (joins) and ?nding data by criteria. ORM will even automatically save your object back database if you wish it too. This document provides a very basic introduction to ORM for the uninitiated. Once ORM is explained, I move on to the more complex task of creating relationships between models. The assumption is that you are already familiar with PHP and MySQL, as well as the Kohana PHP framework. Kohana PHP Object Relational Mapping guide 4 Creating an ORM class How does one create an ORM class to use in Kohana? First you will need to create your table within your database. If you?re planning to use ORM within Kohana you need to adhere to some important naming conventions when naming tables in your database. The main convention you need to remember is singular and pluralisation of data set names. If you have a table for customers within your database, then you will need to call it something like ?customers?(always lowercase). To map this table to a ORM class, you need to ensure your ORM class is named using a singular form,?Customer_Model?for example. (The capitalisation and inclusion of?_Model?is mandatory and should be noted as your model will throw and error if the names are not formatted properly - for more, see http://doc.kohanaphp.com/libraries/orm) Using plural and singular versions of data set names is good practice as your customers table will store many customer records, but each instance of your ORM model will only work on one of those records. This is not always the case when using ORM, as you can also ?nd and return many records, but more on that later. The SQL code for creating your customers table within MySQL is as follows. 1. CREATE TABLE `customers` ( 2. `id` INT( 12 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique id', 3. `title` TEXT NOT NULL COMMENT 'Title', 4. `firstname` TEXT NOT NULL COMMENT 'Firstname', 5. `surname` TEXT NOT NULL COMMENT 'Surname', 6. `email` TEXT NOT NULL COMMENT 'Email Address', 7. PRIMARY KEY ( `id` ) 8. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple customer table' This will create your very simple customer table. The SQL code creates a table with ?ve ?elds, indexed by a unique id that auto-increments. Now you have your database table correctly set up, you can forget SQL and begin creating your ORM class to manipulate data within the this table. To create your ORM class to map to the table you have just created you will need to open your editor and navigate to the application/models folder, then create a new ?le called ?customer.php?. Notice the use of the singular name following our naming convention. All of your ORM classes are going to be extensions of the core ORM class, giving you all the methods of ORM through inheritance. This means that the core contents of your ORM class are going to be very similar across di?ering data sets. Kohana PHP Object Relational Mapping guide 5 Lets set up our Customer_Model for ORM use. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Model extends ORM { 4. 5. 6. } 7. ?> That?s it. Using this code you could now begin manipulating your data within your customers table. However this does not really show the inner workings of ORM, so I?m going to add an additional couple of lines of code. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Model extends ORM { 4. 5. public function __construct( $id = FALSE ) 6. { 7. parent::__construct( $id ); 8. } 9. 10. public function __get( $key ) 11. { 12. return parent::__get( $key ); 13. } 14. 15. public function __set( $key , $value ) 16. { 17. parent::__set( $key , $value ); 18. } 19. 20. } 21. ?> Please note that the inclusion of these three methods is not required, but the inclusion demonstrates what the default ORM method calls are for constructing an instance, plus getting and setting data. When Customer_Model is initiated the __construct() method is called, which in turn calls the ORM __construct() method. This class constructor accepts an?id?as the argument which is referring to the ?eld `customers.id` within the database. If you do not supply the id of a record, ORM will assume this is a new record and insert it as such when you are ?nished. Hopefully the __get() and __set() methods should be self explanatory. A quick note on the __set() method. By default, when you set a value using this method you are only updating your model within PHP, not the database. To set the data in stone you need to invoke the save() method using $my_customer_object->save() for example. Kohana PHP Object Relational Mapping guide 6 If you want to reduce your lines of code or do not want to have to remember to save each time, you can invoke the?auto_save?feature of ORM that saves the instance state upon destruction. To do this manually, you set?auto_save?on your instance after initialisation within a controller. 1. $my_customer_object = new Customer_Model(); 2. $my_customer_object->auto_save = TRUE; You could also achieve this by adding this __destruct() method to your Customer_Model. 1. public function __destruct() 2. { 3. $this->save(); 4. parent::__destruct(); 5. } Or you could set auto_save within your __construct() method in your Customer_Model. 1. public function __construct() 2. { 3. parent::__construct(); 4. $this->auto_save = TRUE; 5. } Which method you use will depend on your application logic. If you always want to automatically save your object state to the database I recommend one of the latter solutions. If you want to automatically save on an ad hoc basis, use the ?rst example. Kohana PHP Object Relational Mapping guide 7 Using Customer_Model with Customer_Controller With your Customer_Model saved, it is now available throughout Kohana for use. All models within application/models folder are automatically loaded for you so all you need to do is call it when ready. Within the controllers folder I have created a ?le called customer.php to control my customer data. This controller needs to show all customer data, display a single record and edit records. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. /** 3. * My Customer Contoller. It?s always good practice to at least hint at what this class 4. * does 5. **/ 6. class Customer_Controller extends Controller { 7. 8. /** 9. * Default controller behaviour, show all records in this case 10. */ 11. public function index() 12. { 13. 14. } 15. 16. /** 17. * View a record 18. */ 19. public function view( $id = FALSE ) 20. { 21. 22. } 23. 24. /** 25. * Edit a record if id supplied, otherwise create a new record (handled by ORM) 26. */ 27. public function edit( $id = FALSE ) 28. { 29. 30. } 31. } This sets up the Customer_Controller class with three methods. Now we can concentrate on the individual methods in turn. Now to set up the default behaviour by adding some code to the index() method. Kohana PHP Object Relational Mapping guide 8 1. public function index() 2. { 3. $customers = new Customer_Model(); 4. $customers = $customers->orderby(?surname?, ?asc?)->find_all(); 5. 6. $output = ?<ol>?; 7. 8. foreach( $customers as $customer ) 9. { 10. $output .= ?<li>{$customer->surname}, {$customer->firstname}</ li>?; 11. } 12. 13. $output .= ?</ol>?; 14. 15. echo $output; 16. } When run, this method will output a list of all registered customers. It currently is not very clever because it will throw an error if there are no customers in the database, but you could ?x that by checking how many results find_all() returned. Here is what the index() method is doing. First of all, it creates a new instance of our Customer_Model. The next line uses ORM?s powerful db builder 1 syntax to ?nd all records, ordered alphabetically by surname. As we are assigning the result back to our Customer_Model instance $customers, this instance is replaced by the result of the query. Of course you can assign the results to another variable if desired. Once the results are collected, we want to output the results. Using an ordered list, the last lines of code output each customer record (Surname, Firstname) to a bu?er $output. Finally the bu?er is printed using echo. When you navigate to: http://<yourservername>/customer/ you will now see a list of all your customers. To concentrate on an individual customer record, we will use the view() method. Kohana PHP Object Relational Mapping guide 9 1 Read more about db builder at http://doc.kohanaphp.com/libraries/orm#using_db_builder_methods 1. public function view( $id = FALSE ) 2. { 3. if( !$id ) 4. { 5. $output = ?You must supply an article id?; 6. } 7. else 8. { 9. $customer = new Customer_Model( $id ); 10. $output = ?<h1>{$customer->surname}, {$customer->firstname}</ h1>?; 11. $output .= ?<p>Email : {$customer->email}</p>?; 12. } 13. 14. echo $output; 15. } The view() method takes one argument, which is the id of the record you wish to load. If no id is supplied then the output informs the user of their mistake - you could use a 404 error here if you liked, but I prefer to be a little more informative. If an id is supplied, a new Customer_Model instance is created using the id submitted. With the data loaded, we can populate our $output with the full name and email of the customer. Finally we can echo our output. This isn?t fully robust as we should check to see if the id supplied is valid, but I?ll come back to this later when I add more methods to Customer_Model. All that is to be done now is create our edit() method. Our edit method is going to either load an existing record and allow the user to edit it, or if record id is omitted then the user will be creating a new record. I am going to use another Kohana PHP helper called Forge to do this and you can read more about all of this at http:// doc.kohanaphp.com/addons/forge . Forge creates and processes forms for you and can perform validation using Kohana PHP?s own validate module. Kohana PHP Object Relational Mapping guide 10 1. public function edit( $id = FALSE ) 2. { 3. // Create a new form using Forge 4. $edit_form = new Forge(); 5. $customer = new Customer_Model( $id ); 6. 7. $edit_form->hidden( ?id? )->value($id); 8. $edit_form->input( ?title? )->label( TRUE )->rules('required| length[2,30]')->value( $customer->title ); 9. $edit_form->input( ?firstname? )->label( TRUE )->rules('required| length[2,50]')->value( $customer->firstname ); 10. $edit_form->input( ?surname? )->label( TRUE )->rules('required| length[2,50]')->value( $customer->surname ); 11. $edit_form->input( ?email? )->label( TRUE )->rules('required| length[2,50]|valid_email_rfc')->value( $customer->email ); 12. $edit_form->submit( ?Submit? ); 13. 14. // If the form is valid 15. if( $edit_form->validate() ) 16. { 17. if( $edit_form->id ) 18. $customer = new Customer_Model( $edit_form->id->value ); 19. else 20. $customer = new Customer_Model(); 21. 22. $customer->firstname = $edit_form->firstname->value; 23. $customer->surname = $edit_form->surname->value; 24. $customer->title = $edit_form->title->value; 25. $customer->email = $edit_form->email->value; 26. 27. if ( $customer->save() ) 28. { 29. echo ?<p>Record saved!</p>?; 30. } 31. else 32. { 33. echo ?<p>Problem saving record</p>?; 34. } 35. } 36. else 37. { 38. echo $edit_form->render(); 39. } 40. 41. } The edit() method is certainly the most complicated, but then it has to do the most. I will step through what is happening here in blocks. First we create a new Forge instance and then set up some ?elds; one hidden id; four inputs for title, ?rstname, surname and email; ?nally a submit button to send the form. Kohana PHP Object Relational Mapping guide 11 3. // Create a new form using Forge 4. $edit_form = new Forge(); 5. $customer = new Customer_Model( $id ); 6. 7. $edit_form->hidden( ?id? )->value($id); 8. $edit_form->input( ?title? )->label( TRUE )->rules('required| length[2,30]')->value( $customer->title ); 9. $edit_form->input( ?firstname? )->label( TRUE )->rules('required| length[2,50]')->value( $customer->firstname ); 10. $edit_form->input( ?surname? )->label( TRUE )->rules('required| length[2,50]')->value( $customer->surname ); 11. $edit_form->input( ?email? )->label( TRUE )->rules('required| length[2,50]|valid_email_rfc')->value( $customer->email ); 12. $edit_form->submit( ?Submit? ); Next we check if the form is valid, which can only be true if the form is submitted and if the form input is valid using the rules de?ned setting up the form. The code ?rst establishes the edit type. 13. // If the form is valid 14. if( $edit_form->validate() ) 15. { 16. if( $edit_form->id ) 17. $customer = new Customer_Model( $edit_form->id->value ); 18. else 19. $customer = new Customer_Model(); 20. 21. $customer->firstname = $edit_form->firstname->value; 22. $customer->surname = $edit_form->surname->value; 23. $customer->title = $edit_form->title->value; 24. $customer->email = $edit_form->email->value; 25. 26. if ( $customer->save() ) 27. { 28. echo ?<p>Record saved!</p>?; 29. } 30. else 31. { 32. echo ?<p>Problem saving record</p>?; 33. } 34. } If the $edit_form->id value is false, then this is a new customer record. Otherwise the customer record is loaded before the values are updated. Once all of the ?elds customer are updated within the object, the customer instance is saved. ORM handles the di?erence in SQL between UPDATE and INSERT for us. Kohana PHP Object Relational Mapping guide 12 If no form has been submitted, then the form we de?ned at the top of the edit() method is printed. 35. else 36. { 37. echo $edit_form->render(); 38. } To use this you can no navigate to: http://<yourservername>/customer/edit/[<record id>] If you supply the optional id then you?ll edit an existing record, otherwise you will create a new record. It should be noted that this is a very basic. You should do more to protect from XSS and other malicious input, as well as better error handling if arguments are not supplied. Validation of the inputs is handled by Forge and it will automatically output the form with highlighted ?elds if your input was incorrect, which is why use of Forge is highly recommended. Kohana PHP Object Relational Mapping guide 13 Extending the behaviour of Customer_Model You will remember that when I set up my Customer_Model class, I rede?ned the __constructor(), __get() and __set() methods from the ORM parent. Currently the methods mimic the behaviour of the parent by calling their parents equivalent methods. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Model extends ORM { 4. 5. public function __construct( $id = FALSE ) 6. { 7. parent::__construct( $id ); 8. } 9. 10. public function __get( $key ) 11. { 12. return parent::__get( $key ); 13. } 14. 15. public function __set( $key , $value ) 16. { 17. parent::__set( $key , $value ); 18. } 19. 20. } 21. ?> I am now going to demonstrate how you can extend your model to your needs. I am going to concentrate on the __set() method. When I set my values for ?rstname, surname and title, I want to ensure that the values are capitalised correctly. To do this I ?rst need to inspect what $key is being set, and then decide what to do - if anything. 1. public function __set( $key, $value ) 2. { 3. if( $key === ?title? || $key === ?firstname? || $key === ?surname? ) 4. { 5. // Ensure the title, firstname or surname are formatted correctly 6. $value = ucwords(strtolower($value)); 7. } 8. 9. parent::__set( $key, $value ); 10. } Now before Customer_Model sets any value, it ?rst checks to see if any of the keys match title, ?rstname or surname. If they do then $value is lowercased and then the ?rst letter of each word is uppercased using the standard PHP functions strtolower() and ucwords(). Finally the data is set using the ORM method as usual. You can perform as many tasks as you like before your set Kohana PHP Object Relational Mapping guide 14 your values in your models. Just ensure you check the keys you are working with before transforming their values. Kohana PHP Object Relational Mapping guide 15 Creating ORM relationships So far I have detailed how ORM deals with single data sets. But what happens if we need to link a customer record to an address or addresses in the database? Well the short answer is ORM can do this to, but you need to set up your parent child relationships in your ORM model as well as within your database. I will demonstrate a?One to many?and?Many to many?relationship. The other variations should become easier to interpret once you?ve read through this chapter. One to many relationship First of all I am going to create an address table that is directly related to my Customer_Model, with Customer_Model being the parent of addresses. This is the simplest of relationships. The address table has to have some key information to ensure ORM works properly for this relationship. The table name has to start with a singular version of the parent followed by underscore and the a plural version of the child name. In addition to this, a ?eld providing the related parent needs to be included. This also follows a strict naming convention. The ?eld name should be named using a singular parent name followed by an underscore and the ?eld name that this record is linked to in the parent table (the id of the customer in this instance). This means a table called customer_addresses is created with a required ?eld name called customer_id to cement the relationship. Make sure your required ?eld name data-type matches the parents. The MySQL code is below. 1. CREATE TABLE `customer_addresses` ( 2. `id` INT( 12 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique id', 3. `customer_id` INT( 12 ) NOT NULL COMMENT 'The id of the customer', 4. `type` TEXT NOT NULL COMMENT 'Type of address, home, work etc', 5. `property` TEXT NOT NULL COMMENT 'Property name or number', 6. `line1` TEXT NOT NULL COMMENT 'First line', 7. `line2` TEXT NOT NULL COMMENT 'Second line', 8. `city` TEXT NOT NULL COMMENT 'City', 9. `postalcode` TEXT NOT NULL COMMENT 'Post code', 10. `country` TEXT NOT NULL COMMENT 'Country', 11. PRIMARY KEY ( `id` ) 12. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple address table' NOTE: Kohana PHP has an inflector helper to help determine the correct plural or singular version of a word, particularly useful for decoding the singular of addresses. Now we need to create an address model and tell both models about the relationships between customers and addresses. Lets create the address model ?rst, naming the ?le containing the model, customer_address.php. Kohana PHP Object Relational Mapping guide 16 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Address_Model extends ORM { 4. 5. // Relationships 6. protected $belongs_to = array(?customer?); 7. 8. } 9. ?> Following the same rules as before, the address model is named Customer_Address_Model. This is because we are using a singular version of customers and a singular version of addresses, which matches the rules stated earlier. 6. protected $belongs_to = array(?customer?); The $belongs_to array has one entry,?customer?. This tells ORM which model this model is a child of. As $belongs_to is an array, you have probably realised you can associate this model with more than one other model. With the address model set up, it is time to inform the customer model that it has some children. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Model extends ORM { 4. 5. // Relationships 6. $has_many = array(?addresses?); 7. 8. public function __construct( $id = FALSE ) 9. { 10. parent::__construct( $id ); 11. } 12. 13. public function __get( $key ) 14. { 15. return parent::__get( $key ); 16. } 17. 18. public function __set( $key, $value ) 19. { 20. if( $key === ?title? || $key === ?firstname? || $key === ?surname? ) 21. { 22. // Ensure the title, firstname or surname are formatted correctly 23. $value = ucwords(strtolower($value)); 24. } 25. 26. parent::__set( $key, $value ); 27. } 28. } 29. ?> Kohana PHP Object Relational Mapping guide 17 Once again the relationship is de?ned before anything else. With the customer model we use the plural version of the model name, as there are many addresses that could relate to this instance. Model will know to look for Customer_Address_Model because you have declared the relationship in $has_many. You have now completed your relationship declaration. Many to many relationship When it comes to many to many relationships the set up slightly di?ers. This time I am going to assume addresses can be shared between customers; two or more customers can use the same address record for example. This time address data is not a direct child of customer data. So the table in MySQL will be more similar to the structure of the customer table. 1. CREATE TABLE `addresses` ( 2. `id` INT( 12 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique id', 3. `type` TEXT NOT NULL COMMENT 'Type of address, home, work etc', 4. `property` TEXT NOT NULL COMMENT 'Property name or number', 5. `line1` TEXT NOT NULL COMMENT 'First line', 6. `line2` TEXT NOT NULL COMMENT 'Second line', 7. `city` TEXT NOT NULL COMMENT 'City', 8. `postalcode` TEXT NOT NULL COMMENT 'Post code', 9. `country` TEXT NOT NULL COMMENT 'Country', 10. PRIMARY KEY ( `id` ) 11. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple address table' Notice that the table name is plural (?addresses?) this time and the ?eld that ties each record to a customer_id is gone. Because all of the relationship data is gone from the addresses table, an additional table is created to map the addresses to their customers. This is the pivot table and has a speci?c naming convention like all other tables. I am choosing that Customers is still parent of Addresses for this example, but you could map them in reverse if your application logic dictated that. As there now are many of both data types in our relationship, the pivot table name is called `customers_addresses` - all plural. Notice there is no `id` ?eld, just the `{related table name}_id` for each table in the join. 1. CREATE TABLE `customers_addresses` ( 2. `customer_id` INT( 12 ) UNSIGNED NOT NULL COMMENT 'Customer id', 3. `address_id` INT( 12 ) UNSIGNED NOT NULL COMMENT 'Address id', 4. PRIMARY KEY (`customer_id`,`address_id`) 5. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple customers pivot table' Kohana PHP Object Relational Mapping guide 18 This completes the database table set up required for many to many joins. To get the join to work within ORM we need to do some modi?cation of the Customers_Model and create a new Addresses_Model. Below is the re?ned Customers_Model ?le with newly updated relationships. I should note that I have also removed the __construct() and __get() functions as they were unchanged from their parents default behaviour. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Model extends ORM { 4. 5. // Relationships 6. $has_and_belongs_to_many = array(?addresses?); 7. 8. public function __set( $key, $value ) 9. { 10. if( $key === ?title? || $key === ?firstname? || $key === ?surname? ) 11. { 12. // Ensure the title, firstname or surname are formatted correctly 13. $value = ucwords(strtolower($value)); 14. } 15. 16. parent::__set( $key, $value ); 17. } 18. } 19. ?> The relationship is now $has_and_belongs_to_many = array(?addresses?). Notice the classes is now plural as the relationship is?to many?. A new ?le called address.php should be created within the application/models folder with the code below. 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Address_Model extends ORM { 4. 5. // Relationships 6. protected $belongs_to_many = array(?customers?); 7. 8. } 9. ?> Now the Address_Model is not a child of any other class, but belongs to many customers as de?ned in $belongs_to_many = array(?customers?). Kohana PHP Object Relational Mapping guide 19 Once your models are in place and correctly linked, ORM will be able to update all tables by using methods for ?nding, adding and removing relationships. These are outlined in the next chapter,?Using ORM relationships in your code?. ORM relationships summary The biggest hurdle when dealing with ORM relationships is getting the naming conventions correct. Producing a database diagram before coding should make the relationships apparent, which will help writing ORM classes and their relationships. The table below outlines the parent and child declarations for the di?erent data types. The { P } under?Many to many? signi?es it requires a pivot table to be created as this is a join. Relationship Parent class header Child class header One to one $has_one = array(?child?) $belongs_to = array(?parent?) One to many $has_many = array(?childs?) $belongs_to = array(?parent?) Many to many { P } $has_and_belongs_to_many = array(?childs?) $belongs_to_many = array(?parents?) Many to one $has_one = array(?child?) $belongs_to_many = array(?parents?) Pivot tables do not require a unique id, but do require the singular {related}_id for each table within the join. The incorrect use of ?childs?is deliberate as Kohana PHP?s In?ector class will not resolve child to children, it will add an?s?to child instead. You can read more about the ORM conventions on the Kohana PHP web site. Kohana PHP Object Relational Mapping guide 20 Using ORM relationships in your code With all relationships set up between models, additional methods within ORM become available. I am not going to go into great detail here, but the following methods become available using the example above. ?nd_related $parent->?nd_related_$child 1. $customer = new Customer_Model(1); 2. $addresses = $customer->find_related_addresses(); $addresses will contain all addresses that belong to this customer. add $parent->add_$child( $instance ) 1. $address = new Customer_Address_Model(); 2. $address->type = ?home?; 3. $address->property = ?1?; 4. $address->line1 = ?Brockle Gate Drive?; 5. $address->city = ?London?; 6. $address->postalcode = ?SE1 1PP?; 7. $address->country = ?United Kingdom?; 8. 9. $customer = new Customer_Model(); 10. 11. // Add $address to $customer 12. $customer->add_address( $address ); 13. 14. $customer->save(); This will add a new address to the $customer instance. When you perform; $customer->save(); all data across models and database tables will be updated. remove $child->remove( ) 15. $customer = new Customer_Model(1); 16. $addresses = $customer->find_related_addresses(); 17. 18. // Remove the first related $address 19. $addresses[0]->remove(); Kohana PHP Object Relational Mapping guide 21 Appendix A - Full Files On the following pages I provide my ?nished ?les in full, which should hopefully help some of you to get going quickly. If you have any comments about the ?les (such as incorrect code), you?re welcome to ?x it and post it back on my blog (http://sam.clark.name) as I?ll be keeping this document up to date. customers.sql SQL Code for the customers table 20. CREATE TABLE `customers` ( 21. `id` INT( 12 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique id', 22. `title` TEXT NOT NULL COMMENT 'Title', 23. `firstname` TEXT NOT NULL COMMENT 'Firstname', 24. `surname` TEXT NOT NULL COMMENT 'Surname', 25. `email` TEXT NOT NULL COMMENT 'Email Address', 26. PRIMARY KEY ( `id` ) 27. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple customer table' customer_addresses.sql SQL Code for the customer_addresses table 1. CREATE TABLE `customer_addresses` ( 2. `id` INT( 12 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique id', 3. `customer_id` INT( 12 ) NOT NULL COMMENT 'The id of the customer', 4. `type` TEXT NOT NULL COMMENT 'Type of address, home, work etc', 5. `property` TEXT NOT NULL COMMENT 'Property name or number', 6. `line1` TEXT NOT NULL COMMENT 'First line', 7. `line2` TEXT NOT NULL COMMENT 'Second line', 8. `city` TEXT NOT NULL COMMENT 'City', 9. `postalcode` TEXT NOT NULL COMMENT 'Post code', 10. `country` TEXT NOT NULL COMMENT 'Country', 11. PRIMARY KEY ( `id` ) 12. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple address table' Kohana PHP Object Relational Mapping guide 22 addresses.sql SQL Code for the addresses table (Many to many relationship) 1. CREATE TABLE `addresses` ( 2. `id` INT( 12 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique id', 3. `type` TEXT NOT NULL COMMENT 'Type of address, home, work etc', 4. `property` TEXT NOT NULL COMMENT 'Property name or number', 5. `line1` TEXT NOT NULL COMMENT 'First line', 6. `line2` TEXT NOT NULL COMMENT 'Second line', 7. `city` TEXT NOT NULL COMMENT 'City', 8. `postalcode` TEXT NOT NULL COMMENT 'Post code', 9. `country` TEXT NOT NULL COMMENT 'Country', 10. PRIMARY KEY ( `id` ) 11. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple address table' customers_addresses.sql SQL Code for the customers_addresses pivot table (Many to many relationship) 1. CREATE TABLE `customers_addresses` ( 2. `customer_id` INT( 12 ) UNSIGNED NOT NULL COMMENT 'Customer id', 3. `address_id` INT( 12 ) UNSIGNED NOT NULL COMMENT 'Address id', 4. PRIMARY KEY (`customer_id`,`address_id`) 5. ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT = 'A very simple customers pivot table' Kohana PHP Object Relational Mapping guide 23 application/models/customer.php PHP code for Customer_Model (Many to many relationship) 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Model extends ORM { 4. 5. // Relationships 6. $has_and_belongs_to_many = array(?addresses?); 7. 8. public function __set( $key, $value ) 9. { 10. if( $key === ?title? || $key === ?firstname? || $key === ?surname? ) 11. { 12. // Ensure the title, firstname or surname are formatted correctly 13. $value = ucwords(strtolower($value)); 14. } 15. 16. parent::__set( $key, $value ); 17. } 18. } 19. ?> application/models/customer_address.php PHP code for Customer_Address_Model (Many to one relationship) 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Customer_Address_Model extends ORM { 4. 5. // Relationships 6. protected $belongs_to = array(?customer?); 7. 8. } 9. ?> application/models/address.php PHP code for Address_Model (Many to many relationship) 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. 3. class Address_Model extends ORM { 4. 5. // Relationships 6. protected $belongs_to_many = array(?customers?); 7. 8. } 9. ?> Kohana PHP Object Relational Mapping guide 24 application/controllers/customer.php PHP code for Customer_Controller 1. <?php defined('SYSPATH') or die('No direct script access.'); 2. /** 3. * My Customer Contoller. It?s always good practice to at least hint at what this class 4. * does 5. **/ 6. class Customer_Controller extends Controller { 7. 8. /** 9. * Default controller behaviour, show all records in this case 10. */ 11. public function index() 12. { 13. $customers = new Customer_Model(); 14. $customers = $customers->order_by(?surname?, ?asc?)->find_all(); 15. 16. $output = ?<ol>?; 17. 18. foreach( $customers as $customer ) 19. { 20. $output .= ?<li>{$customer->surname}, {$customer->firstname}</ li>?; 21. } 22. 23. $output .= ?</ol>?; 24. 25. echo $output; 26. } 27. /** 28. * View a record 29. */ 30. public function view( $id = FALSE ) 31. { 32. if( !$id ) 33. { 34. $output = ?You must supply an article id?; 35. } 36. else 37. { 38. $customer = new Customer_Model( $id ); 39. $output = ?<h1>{$customer->surname}, {$customer->firstname}</ h1>?; 40. $output .= ?<p>Email : {$customer->email}</p>?; 41. } 42. 43. echo $output; 44. } 45. /** 46. * Edit a record if id supplied, otherwise create a new record (handled by ORM) 47. */ 48. public function edit( $id = FALSE ) 49. { 50. // Create a new form using Forge 51. $edit_form = new Forge(); 52. $customer = new Customer_Model( $id ); 53. Kohana PHP Object Relational Mapping guide 25 54. $edit_form->hidden( ?id? )->value($id); 55. $edit_form->input( ?title? )->label( TRUE )->rules('required| length[2,30]')->value( $customer->title ); 56. $edit_form->input( ?firstname? )->label( TRUE )->rules('required| length[2,50]')->value( $customer->firstname ); 57. $edit_form->input( ?surname? )->label( TRUE )->rules('required| length[2,50]')->value( $customer->surname ); 58. $edit_form->input( ?email? )->label( TRUE )->rules('required| length[2,50]|valid_email_rfc')->value( $customer->email ); 59. $edit_form->submit( ?Submit? ); 60. 61. // If the form is valid 62. if( $edit_form->validate() ) 63. { 64. if( $edit_form->id ) 65. $customer = new Customer_Model( $edit_form->id->value ); 66. else 67. $customer = new Customer_Model(); 68. 69. $customer->firstname = $edit_form->firstname->value; 70. $customer->surname = $edit_form->surname->value; 71. $customer->title = $edit_form->title->value; 72. $customer->email = $edit_form->email->value; 73. 74. if ( $customer->save() ) 75. { 76. echo ?<p>Record saved!</p>?; 77. } 78. else 79. { 80. echo ?<p>Problem saving record</p>?; 81. } 82. } 83. else 84. { 85. echo $edit_form->render(); 86. } 87. 88. } 89. } Kohana PHP Object Relational Mapping guide 26

PARTAGER SUR

Envoyer le lien par email
761
READS
2
DOWN
0
FOLLOW
3
EMBED
DOCUMENT # TAGS
#kohana  #ORM 

CC BY


DOCUMENT # INDEX
PHP 
img

Partagé par  ced

 Suivre

Auteur:
Source:Non communiquée