Добавление условий в Containable в CakePHP
Раньше я полагался на рекурсив, но для некоторых я не нашел решения, а потом обнаружил, что Containable отлично подходит для них.
Я занимаюсь разработкой сайта для обзора фильмов. При этом мне нужно показать список фильмов, относящихся к определенному жанру.
У меня есть этот код ниже:
//example
$genre = "drama";
$options = array(
'contain' => array(
'Movie',
'MoveiGenre.Genre' => array(
'conditions' => array('MovieGenre.Genre.name = "'.$genre.'"')
),
'MovieGenre.Genre.name'
),
'recursive' => 2,
'limit' => 10
);
$this->paginate = $options;
$this->set('movies',$this->paginate());
Настоящая проблема начинается здесь, я получаю все фильмы, даже если это не связано с жанром "драма". Куда я иду не так?
Позвольте мне объяснить таблицу базы данных:
Таблица: фильмы
----------------------------
| id | title | description |
----------------------------
| 1 | mov1 | something1 |
| 2 | mov2 | something2 |
| 3 | mov3 | something3 |
----------------------------
Таблица: жанры
---------------
| id | name |
---------------
| 1 | drama |
| 2 | sci-fi |
| 3 | comedy |
---------------
Таблица: movie_genres
-------------------------------
| id | movie_id | genre_id |
-------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
-------------------------------
Здесь вы можете видеть, что один movie_id имеет несколько genre_id. Я должен получить только mov1
но я получаю оба фильма в массиве.
~~ РЕДАКТИРОВАТЬ ~~ упс! извините забыл упомянуть, я использую этот код в MoviesController
, Все 3 таблицы имеют соответствующий контроллер. Поэтому, пожалуйста, предложите мне, в каком контроллере я могу использовать.
EDIT: 2
class Movie extends AppModel {
public $displayField = 'title';
public $actsAs = array('Containable');
public $hasMany = array(
'MovieGenre' => array(
'className' => 'MovieGenre',
'foreignKey' => 'movie_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
'MovieLanguage' => array(
'className' => 'MovieLanguage',
'foreignKey' => 'movie_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);
}
Модель: Жанр
class Genre extends AppModel {
public $displayField = 'name';
public $hasAndBelongsToMany = array(
'Movie' => array(
'className' => 'Movie',
'joinTable' => 'movie_genres',
'foreignKey' => 'genre_id',
'associationForeignKey' => 'movie_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
Модель: MovieGenre
class MovieGenre extends AppModel {
public $belongsTo = array(
'Movie' => array(
'className' => 'Movie',
'foreignKey' => 'movie_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Genre' => array(
'className' => 'Genre',
'foreignKey' => 'genre_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
1 ответ
TLDR: выполните поиск по жанру и укажите его фильмы или используйте joins() - поиск по фильмам и содержание жанра с условиями не будут работать для получения желаемых результатов.
Объяснение:
Ниже приведен исправленный код "содержать", но, что более важно, выполнение "содержимого" в жанре не вернет искомых результатов.
Что он делает - ограничивает содержащиеся жанры в зависимости от вашего состояния... поэтому он будет тянуть ВСЕ фильмы и содержать жанры, которые соответствуют $genre
,
Решения (в зависимости от ваших потребностей):
Решение 1)
- Сделать
find()
по жанру, с условием, и содержат его фильмы. Это потянет жанр, который соответствует, тогда только фильмы, которые связаны с этим.
Решение 2) - тот, который я бы порекомендовал
- Используйте 'joins()':
$conditions = array();
$conditions['joins'] = array(
array(
'table' => 'genres_movies', //or movie_genres if you've specified it as the table to use
'alias' => 'GenresMovie',
'type' => 'INNER'
'conditions' => array(
'GenresMovie.movie_id = Movie.id'
)
),
array(
'table' => 'genres',
'alias' => 'Genre',
'type' => 'INNER',
'conditions' => array(
'Genre.id = GenresMovie.genre_id',
'Genre.name = "' . $genre . '"'
)
)
);
$this->Movie->find('all', $conditions);
Ваш отредактированный (исправленный ИМО) пример "содержит"
//edited example
$genre = "drama";
$options = array(
'contain' => array(
'Genre' => array(
'conditions' => array('Genre.name' => $genre)
)
),
'recursive' => -1,
'limit' => 10
);
$this->paginate = $options;
$this->set('movies', $this->paginate('Movie'));
- вы не "содержите" модель, по которой вы выполняете поиск (т.е. я удалил "Movie" из вашего массива размещения).
- вы содержите только модели, а не такие вещи, как "MovieGenre.Genre" (я не могу представить, когда вы будете использовать сцепленные модели ".")
- для использования Containable для рекурсивного использования должно быть -1 - вы должны установить его в -1 в AppModel и забыть о рекурсивном - это плохо