This is documentation for Kohana v2.3.x.
Todo |
---|
At first glance it may seem as though has_one
and belongs_to
serve the same purpose and can be used interchangeably - this is not the case. The difference between the two is in the location of the foreign key.
class User_Model extends ORM { protected $has_one = array('portrait'); } class Portrait_Model extends ORM { protected $belongs_to = array('user'); }
In the above example, each user has one (and only 1) portrait. Each portrait belongs to only 1 user. The portraits table should contain the foreign key referencing the user, user_id
. In a has_one
and belongs_to
1-to-1 relationship, the foreign key always lies in the model containing the belongs_to declaration (in this case, the Portrait_Model's table).
Many times the default relationships used within ORM do not effectively communicate the true meaning. In the example used previously in the Getting Started section, users can be related to blog posts as both authors and editors. To define this dual-relationship an alias
can be used to allow multiple users to have relationships to the post. Aliasing allows multiple objects of the same type to be related to another object. Aliasing can also be used to change the name of the related object in order to more effectively communicate the purpose of the relationship more clearly.
The array key is the alias name and the array value, the model name that it maps to.
class Blog_Post_Model extends ORM { protected $belongs_to = array('author' => 'user', 'editor' => 'user'); }
Now we can access both the author and the editor of the blog post like this:
$post = ORM::factory('blog_post', 1); echo $post->author->username; echo $post->editor->username;
The blog_posts
database table would have 2 columns now, blog_posts.author_id
and blog_posts.editor_id
, and both would have values that exist in users.id
.
Note: Aliases can also be used in reverse within the has_one
relationship as well.
If you want similar functionality to has_and_belongs_to_many, but want to have additional data/columns stored in the pivot table for the relationships, you can use a has_many through
relationship. This looks similar to aliasing:
class Post_Model extends ORM { protected $has_many = array('categories'=>'posts_categories'); } class Posts_Category_Model extends ORM { }
Above the Post_Model
is linked to different categories through the posts_categories
pivot table. You must also create a model for the pivot table Posts_Category_Model
. You can now access both the categories and the pivot table by using the following:
$post = ORM::factory('post', $post_id); foreach ($post->categories as $category) { ... } foreach ($post->posts_categories as $pivot) { ... }
Note: You should have an ID column for the pivot table (which, unlike has_and_belongs_to_many
, doesn't require one)
Consider this scenario. You have a system that stores articles, each article can have multiple authors and contributors. Authors and contributors are the same type of object, just a different association to an article. To accomplish this sort of functionality, you would have the core two tables; articles and authors. You would also have TWO pivot tables titled authors_articles and contributors_articles. Each pivot table would contain the columns; id, article_id and author_id.
Your Article_Model
would look something like this:
class Article_Model extends ORM { protected $has_and_belongs_to_many = array('authors_articles' => 'authors','contributors_articles' => 'contributors'); }
In the Author_Model
and Contributor_Model
you will need to override the $table_name value, so both point to the author table.
class Author_Model extends ORM { protected $table_name = 'authors'; } class Contributor_Model extends ORM { protected $table_name = 'authors'; }
ORM uses lazy loading by default, which defers initialization of an object until it is needed. This typically results in an additional query when related data is requested in a one-to-one relationship. The with()
method in ORM can be used to force the use of a JOIN in a one-to-one relationship, resulting in only one query being called. You can also bind nested one-to-one relationships using a colon.
// This uses 1 SQL query to fetch the user, associated city, and associated country. $users = ORM::factory('user')->with('city')->with('city:country')->find_all(); foreach($users as $user) { echo $user->city->country->name; }
You can also set the $load_with
property of the ORM model to bind automatically.
class User_Model extends ORM { protected $load_with = array('city'); }
By default, ORM uses the id column as the identifier for the unique row within the database. However it is possible for ORM to load an object using a different unique key from your table. ORM uses a method called unique_key
to load data and this method can be overloaded within your ORM model to allow other columns to be used.
The example demonstrates the use of unique_key
within an ORM model. The id is checked for data type. If the datatype is not a digit and is a string, the column shortname will be used to load the model.
public function unique_key($id = NULL) { if ( ! empty($id) AND is_string($id) AND ! ctype_digit($id) ) { return 'shortname'; } return parent::unique_key($id); }
If you intend to you use custom unique keys within your application, it is a good idea to ensure you correctly index all columns being used as a unique identifier. This will ensure that as your application scales, performance is not adversely effected.
Assuming the homepage record has an ID of 1, including the unique_key method within your ORM model allows the following constructor methods in your code.
// Using the ORM::factory method $my_page = ORM::factory( 'Page', 'homepage' ); // Using the standard constructor $my_other_page = new Page_Model( 'homepage' ); $my_old_method = new Page_Model( 1 );
$my_page
, $my_other_page
and $my_old_method
will all contain the same record.
If you have a table that uses a primary key other than id
, you can simply override the $primary_key
property in your model to have ORM use your custom primary key to load your model.
In the following example, a Geo model is defined with a primary key of zip
as opposed to the standard ORM id
.
class Geo extends ORM { protected $primary_key = 'zip'; }
ORM tree is an ORM extension that allows creating an object relationship with itself. Typical use of this library can be a category hierarchy, in other words parent ↔ childrens link.
Your database schema will need to include a parent ID column.
class Category_Model extends ORM_Tree { protected $ORM_Tree_children = 'categories'; // Set the column which holds this models parent // Default is parent_id protected $ORM_Tree_parent_key = 'parent_id'; }
echo Kohana::debug(ORM::factory("Category", 42)->children->as_array()); //returns the children for the category with id 42
echo Kohana::debug(ORM::factory("Category", 4)->parent->name); //returns the parent name of the category with ID 4
ORM has the ability to validate and save data automatically. Any errors encountered during validation can be used to feedback to the view using the standard Validation Library error reporting method.
To use Validation in an ORM model a set of validation rules and filters must be defined within a public method called validate, which overloads the ORM::validate() method. See Examples: Combining ORM and Validation for further details and code samples.