📜 ⬆️ ⬇️

CakePHP 1.2 Containable Behavior

The new release of the wonderful framework CakePHP will bring another feature that can greatly simplify the life of developers - Containable Behavior. Especially this feature will be useful when working with models with a large number of associations. Consider the work of this behavior on the example of the video catalog project. To begin with, our models:
<?php

class
Film extends AppModel {

var
$name = 'Film' ;

var $hasMany = array(
'FilmPicture' => array( 'className' => 'FilmPicture' ,
'foreignKey' => 'film_id' ,
),
'FilmVariant' => array( 'className' => 'FilmVariant' ,
'foreignKey' => 'film_id' ,
),
);

var
$hasAndBelongsToMany = array(
'Country' => array( 'className' => 'Country' ,
'joinTable' => 'countries_films' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'country_id' ,
),
'Genre' => array( 'className' => 'Genre' ,
'joinTable' => 'films_genres' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'genre_id' ,
),
'Person' => array( 'className' => 'Person' ,
'joinTable' => 'films_persons' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'person_id' ,
),
'Publisher' => array( 'className' => 'Publisher' ,
'joinTable' => 'films_publishers' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'publisher_id' ,
),
);

var
$belongsTo = array(
'FilmType' => array( 'className' => 'FilmType' ,
'foreignKey' => 'film_type_id' ,
)
);
var
$hasOne = array(
'MediaRating' => array( 'className' => 'MediaRating' ,
'foreignKey' => 'object_id' ,
'conditions' => 'MediaRating.type="film"' ,
)
);

var
$actsAs = array( 'Containable' );
}

?>
<?php

class
Film extends AppModel {

var
$name = 'Film' ;

var $hasMany = array(
'FilmPicture' => array( 'className' => 'FilmPicture' ,
'foreignKey' => 'film_id' ,
),
'FilmVariant' => array( 'className' => 'FilmVariant' ,
'foreignKey' => 'film_id' ,
),
);

var
$hasAndBelongsToMany = array(
'Country' => array( 'className' => 'Country' ,
'joinTable' => 'countries_films' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'country_id' ,
),
'Genre' => array( 'className' => 'Genre' ,
'joinTable' => 'films_genres' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'genre_id' ,
),
'Person' => array( 'className' => 'Person' ,
'joinTable' => 'films_persons' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'person_id' ,
),
'Publisher' => array( 'className' => 'Publisher' ,
'joinTable' => 'films_publishers' ,
'foreignKey' => 'film_id' ,
'associationForeignKey' => 'publisher_id' ,
),
);

var
$belongsTo = array(
'FilmType' => array( 'className' => 'FilmType' ,
'foreignKey' => 'film_type_id' ,
)
);
var
$hasOne = array(
'MediaRating' => array( 'className' => 'MediaRating' ,
'foreignKey' => 'object_id' ,
'conditions' => 'MediaRating.type="film"' ,
)
);

var
$actsAs = array( 'Containable' );
}

?>
<?php
class FilmVariant extends AppModel {

var
$name = 'FilmVariant' ;

var
$belongsTo = array(
'Film' ,
'VideoType'
);

var
$hasMany = array(
'FilmFile' ,
'Track'
);

}
?>
<?php
class FilmVariant extends AppModel {

var
$name = 'FilmVariant' ;

var
$belongsTo = array(
'Film' ,
'VideoType'
);

var
$hasMany = array(
'FilmFile' ,
'Track'
);

}
?>

I will not give the entire list of models, they look the same.
The key point is to connect the Containable itself in the model:

<?php
var $actsAs = array( 'Containable' );
?>


Or it can be connected on the fly in the controller:

<?php
$this
-> Film -> Behaviors -> attach ( 'Containable' );
?>


Now let's see how we can simplify our lives :) Here is the usual way to get all the films with associations:
<?php
$this
-> Film -> recursive = 2 ;
$this -> Film -> find ( 'all' )
?>


In this case, we will receive not only films, but also all associated entries + entries of second-level associations, agree that this is needed very rarely. Now, let's see what Containable offers:
')
<?php
$this
-> Film -> contain ( 'Country' );
$this -> Film -> find ( 'all' );
// , find()
$this -> Film -> find ( 'all' , array( 'contain' => 'Country' ));
?>

By these calls we will receive only a list of films with associated countries.

Level up :)
I, in principle, need only the name of the film’s country. So why do I need everything else?
<?php
$this
-> Film -> contain ( 'Country.title' );
$this -> Film -> find ( 'all' );
// , find()
$this -> Film -> find ( 'all' , array( 'contain' => 'Country.title' ));
?>

Level up!
In addition to the country, for the list of films I also have posters for my husband ... We get:
<?php
$this
-> Film -> find ( 'all' ,
array(
'contain' => 'Country.title' ,
'FilmPicture' =>
array(
'conditions' => array( 'type' => 'poster' ),
'fields' => array( 'FilmPicture.file_name' , 'FilmPicture.id' ))));
?>


Final Strike :)

Pagination for new paginate () method

<?php
$this
-> paginate = array( 'Film' =>
array(
'contain' =>
array(
'FilmType' ,
'Genre' ,
'FilmPicture' => array( 'conditions' => array( 'type' => 'poster' )),
'Country' ,
'Person' => array( 'conditions' => array( 'FilmsPerson.profession_id' => array( 1 , 3 , 4 ))),
'MediaRating' ),
'order' => array( 'Film.modified DESC' ),
'conditions' => array( 'Film.active' => 1 ),
'limit' => 12 ));
$this -> set ( 'films' , $this -> paginate ());
?>


Retrieving data for a single movie. Please note that if you specify a recursion level equal to two, only those models that we have specified are drawn, namely, the files and the type of video for the movie variant:
<?php
$this
-> Film -> recursive = 2 ;
$this -> Film -> contain (array( 'FilmType' ,
'Genre' ,
'FilmPicture' ,
'Country' ,
'FilmVariant' => array( 'FilmFile' , 'VideoType' ),
'MediaRating' ));
$film = $this -> Film -> read ( null , $id );
?>


Traditional: this is my PPNH, do not judge strictly :)

Source: https://habr.com/ru/post/38675/


All Articles