CakePHP: Manual/Developing/Models/Retrieving

Получение данных

find

find($type, $params)

Параметру $type можно присваивать что-либо из 'all', 'first', 'count', 'list', 'neighbors' or 'threaded'. 'first' – значение по умолчанию.

$params – это массив с любыми, ниже представленными опциями, в качестве индекса:

<?php

array(

    'conditions' => array('Model.field' => $thisValue), //массив условий

    'recursive' => 1//int

    'fields' => array('Model.field1','Model.field2'), //массив имен полей

    'order' => array('Model.created','Model.field3 DESC'),//строка или массив, определяющие порядок

    'group' => array('Model.field'), //поля для GROUP BY

    'limit' => n//int

    'page' => n//int

    'callbacks' => true //другие возможные значения: false, 'before', 'after'

)

?>

Если вы используете find('list'), индекс 'fields' в $params определяет индекс, значение и группу

<?php

// сгенерированный список будет проиндексирован по Post.id, со значением Post.title

$this->Post->find('list', array('fields'=>'Post.title'));

 

//сгенерированный список будет проиндексирован по Post.slug, со значением Post.title

$this->Post->find('list', array('fields'=>array('Post.slug''Post.title')));

 

// сгенерированный список будет сгруппирован по Post.author_id, и каждая группа

// проиндексирована по Post.id, со значением Post.title

$this->Post->find('list', array('fields'=>array('Post.id''Post.title''Post.author_id')));

?>

Если вы хотите использовать метку optgroup из родительской таблицы, установите в параметрах 'recursive'=>1.

Если вы используете find('neighbors'), то индекс 'field' в $params определяет поле для анализа, и индекс 'value' в массиве $params определяет значение по которому определяется следующий и предыдущий. Обратите внимание, что индексы 'field' и 'value' не используются для find('all') – это специальные символы для find('neighbors').

<?php

// допустим у нас есть id от 1 до 10, мы увидим prev, установленное в 1 и next, установленное в 3

$this->Post->id 2;

$one $this->Post->find('neighbors');

// Для получения соседних данных, используя другое поле..

$two $this->Post->find('neighbors', array('field'=>'Post.title''value'=>$data['Post']['title']));

// Обратите внимание, что если используете индекс 'fields' для поиска, 

//то должен быть определен 'field', иначе это не сработает

$three $this->Post->find('neighbors', array('field'=>'Post.title''value'=>$data['Post']['title'], 'fields'=>array('id''title'));

?>

Для обратной совместимости, find также принимает предыдущий синтаксис:

find(string $conditions, array $fields, string $order, int $recursive)

Если вам необходимо использовать SQL операторы такие, как в (MySQL) DISTINCT, MAX, MIN, и др., то вы можете определить их как часть индекса 'fields', например так:

$this->Product->find('all', array('fields'=>'DISTINCT (Product.name) AS product_name'));

Если вы пытаетесь найти уникальную комбинацию полей (например, сгенерируйте sql, подобный, приведенному ниже)

SELECT DISTINCT Client, Job, Description

FROM JOB

WHERE Active=1

GROUP BY Client, Job, Description

ORDER BY Client

Делайте следующее...

<?php

$condition = array(

            'conditions' => array('Model.Active =' => 1),

            'fields' => array('DISTINCT Model.Client''Model.Job''Model.Description'),

            'order' => array('Model.Client'),

            'group' => array('Model.Client''Model.Job''Model.Description'));

$this->Model->find('all'$condition);

?>


findAll

findAll(string $conditions, array $fields, string $order, int $limit, int $page, int $recursive)

findAll оставлено для совместимости, используйте find('all').

Возвращает указанные поля (количество записей – до, указанного в $limit), согласно условию $conditions (если есть), начинает список со страницы $page (по умолчанию, страница 1). Если в таблице нет указанных полей, то возвращается пустой массив.

Условия $conditions должны быть сформированы так, как бы они выглядели в SQL выражении: $conditions = "Pastry.type LIKE '%cake%' AND Pastry.created_on > '2007–01–01'", например. Добавление в условиях префикса в виде имени модели ('Pastry.type' rather than just 'type') – это хорошая практика, особенно когда в запросе происходит выборка ассоциированных данных.

Присвоение параметру $recursive целого числа заставляет findAll() выбирать данные согласно поведению, описанному ранее в разделе «Переменные модели. $recursive». Не забывайте вручную добавлять требуемые поля внешних ключей в массив $fields, как там описано.

Данные из findAll() возвращаются в виде массива, согласно этому основному формату:

Array

(

    [0] => Array

        (

            [ModelName] => Array

                (

                    [id] => 83

                    [field1] => value1

                    [field2] => value2

                    [field3] => value3

                )

            [AssociatedModelName] => Array

                (

                    [id] => 1

                    [field1] => value1

                    [field2] => value2

                    [field3] => value3

                )

        )

    [1] => Array

        (

            [ModelName] => Array

                (

                    [id] => 85

                    [field1] => value1

                    [field2] => value2

                    [field3] => value3

                )

            [AssociatedModelName] => Array

                (

                    [id] => 2

                    [field1] => value1

                    [field2] => value2

                    [field3] => value3

                )

        )

)


findAllBy

findAllBy<fieldName>(string $value)

Эти волшебные функции могут использоваться, как методы быстрого поиска в ваших таблицах по определенному полю. Просто добавьте имя поля (в CamelCase формате ) в конец этой функции, и передайте критерий поиска по этому полю первым параметром.


findBy

findBy<fieldName>(string $value)

Эти волшебные функции могут использоваться, как методы быстрого поиска в ваших таблицах по определенному полю. Просто добавьте имя поля (в CamelCase формате ) в конец этой функции, и передайте критерий поиска по этому полю первым параметром.

PHP5 findAllBy<x> Example Corresponding SQL Fragment
$this->Product->findAllByOrderStatus('3'); Product.order_status = 3
$this->Recipe->findAllByType('Cookie'); Recipe.type = 'Cookie'
$this->User->findAllByLastName('Anderson');User.last_name = 'Anderson'
$this->Cake->findById(7);Cake.id = 7
$this->User->findByUserName('psychic');User.user_name = 'psychic'

Пользователи PHP4 должны использовать эту функции, немного отличную, из-за некоторой нечувствительности регистра в PHP4:

PHP4 findAllBy<x> ExampleCorresponding SQL Fragment
$this->Product->findAllByOrder_status('3');Product.order_status = 3
$this->Recipe->findAllByType('Cookie');Recipe.type = 'Cookie'
$this->User->findAllByLast_name('Anderson');User.last_name = 'Anderson'
$this->Cake->findById(7);Cake.id = 7
$this->User->findByUser_name('psychic');User.user_name = 'psychic'

findBy() функции подобны find('first',...), так же, как и findAllBy() функции подобны find('all',...).

В обоих случаях, возвращаемый результат – это массив в таком же формате, как и результат find() или findAll(), соответственно.


findNeighbours

findNeighbours(string $conditions, mixed $field, string $value)

findNeighbours оставлена для совместимости, используйте find('neighbors').

Этот ускоренный метод создает массив, содержащий значения, полезные в генерации ссылок 'Предыдущий' и 'Следующий' в отображении.

Метод возвращает записи данных, на основе значений в параметрах $field и $value. Дальнейшее уточнение может быть сделано с помощью параметра $conditions.

Например, вы вызываете функцию таким образом:

<?

$conditions = array('Article.status' => 'опубликована');

$field = array('date''id');

$value '2008-03-24';

$this->Article->findNeighbours$conditions$field$value ) );

?>

Результирующий массив будет содержать значения для полей 'date' и 'id' из статей со статусом «опубликована», и дата которых до и после даты '2008–03–24'.

Array

(

    [prev] => Array ([Article] => 

             Array ([date] => 2008-03-20, [id] => 99 )

    ),

    [next] => Array ( [Article] => 

             Array( [date] => 2008-03-27, [id] => 15 )

    )

);

Обратите внимание, что сравнение было сделано по полю с датой, и значения id не использовались для определения соседних данных.

Этот метод также может быть вызван со значением параметра $field в виде отдельной строки. Когда используется массив, то первое указанное поле будет полем, используемым в сравнительном запросе.

<?

class ImagesController extends AppController {

    function view($id) {

        // Говорим, что мы хотим видеть изображение (image)...

        $this->set('image'$this->Image->findById($id);

        // Но еще мы хотим ссылки на предыдущее и следующее изображение...

        $this->set(

            'neighbors'

            $this->Image->findNeighbours(null'id'$id);

        )

    }

}

?>

Это дает нам полный массив $image['Image'], вместе с $neighbors['prev']['Image']['id'] и $neighbors['next']['Image']['id'] для использования в отображении.


query

query(string $query)

Обычные SQL запросы могут быть сделаны с помощью метода модели query().

Если вы используете собственные SQL запросы в вашем приложении, то проверьте наличие библиотеки Sanitize, которая помогает очистить, предлагаемые пользователем данные, от SQL-инъекций и скриптовых аттак.

Для того, чтобы ваш запрос не кэшировался, присваивайте второму параметру значение false. Пример: query($query, $cachequeries = false).

query() использует имя таблицы в запросе, как индекс массива для возвращаемых данных, а не имя модели. Например,

<? $this->Picture->query("SELECT * FROM pictures LIMIT 2;"); ?>

может вернуть

Array

(

    [0] => Array

        (

            [pictures] => Array

                (

                    [id] => 1304

                    [user_id] => 759

                )

        )

    [1] => Array

        (

            [pictures] => Array

                (

                    [id] => 1305

                    [user_id] => 759

                )

        )

)

Для использования имя модели, в виде индекса массива, и получения соответствующего результата, запрос должен быть переписан:

<? $this->Picture->query("SELECT * FROM pictures AS Picture LIMIT 2;"); ?>

Результат:

Array

(

    [0] => Array

        (

            [Picture] => Array

                (

                    [id] => 1304

                    [user_id] => 759

                )

        )

    [1] => Array

        (

            [Picture] => Array

                (

                    [id] => 1305

                    [user_id] => 759

                )

        )

)


generateList

generateList(string $conditions, string $order, int $limit, string $keyPath, string $valuePath)

generateList устарела и заменена использованием find('list'), или find('all'), совмещенная с вызовом Set::combine().

Эта функция используется для быстрого получения списка пар ключ/значение – особенно удобно для создания HTML тэга select из списка ваших моделей. Используйт параметры $conditions, $order, и $limit так же, как бы вы их использовали для findAll() запроса.

Если $primaryKey и $displayField определены в модели, то нет необходимости передавать два последних параметра, т.к. они действуют, как $keyPath и $keyValue, соответственно. Также, если ни $keyPath ни $displayField не передаются, то CakePHP попытается загрузить информацию, используя 'title' или 'name'.

Параметры $keyPath и $valuePath определяют где искать ключи и значения для сгенерированного списка. Для примера, если вы хотите сгенерировать список на основе вашей модели Role, с ключом integer id, полный вызов должен выглядеть, например, так.:

<?

$this->Role->generateList(

    null

    'role_name ASC'

    null

    '{n}.Role.id'

    '{n}.Role.role_name'

);

//Это вернет, нечто наподобие:

array(

    '1' => 'Head Honcho',

    '2' => 'Marketing',

    '3' => 'Department Head',

    '4' => 'Grunt'

);

?>

Многих людей немного смущает синтаксис '{n}', используемый generateList(). Не беспокойтесь, это символ-заполнитель для переключения между источниками данных модели. Об этом будет написано далее.


findCount

findCount(string $conditions, int $recursive)

Этот метод оставлен для совместимости, используйте find('count').

Возвращает количество записей, удовлетворяющих заданному условию. Используйте параметр $recursive, для того, чтобы CakePHP выбирал больше (или меньше) уровней ассоциированных моделей.


field

field(string $name, array $conditions = null, string $order = null)

Возвращает значение отдельно взятого поля, определенного как $name, из первой записи результата выборки, удовлетворяющей условию $conditions и отсортированной согласно $order. Если никаких условий не указано, но определен id, то функция вернет значение поля $name для данного id. Если нет подходящей записи, то функция вернет false.

<?

$model->id 22;

echo $model->field('name'); // выводит значение поля name для id=22

// выводит значение поля name записи, созданной последней.

echo $model->field('name', array('created <' => date('Y-m-d H:i:s')), 'created DESC'); 

?>


Комплексные условия поиска

Множество поисковых запросов модели требуют передачу условий тем или иным способом. Самый простой способ – это использовать SQL оператор WHERE. Если вам понадобится больше управления, то вы может использовать массивы.

Использование массивов яснее и легче воспринимается, а также, делает очень простым создание запросов. В синтаксисе, также разделяются элементы вашего запроса (поля, значения, операторы, и др.) на отдельные управляемые части. Это позволяет CakePHP генерировать наиболее эффективные запросы, обеспечивать правильный SQL синтаксис, и корректно завершать каждую часть запроса.

Базовый запрос, основанный на массиве, выглядит так:

<?

$conditions = array("Post.title" => "Это пост");

//Пример использования с моделью:

$this->Post->find($conditions);

?>

Структура здесь говорит сама за себя: запрос найдет любой пост, название которого равно «Это пост». Обратите внимание, что мы можем использовать только “title” в качестве имени поля, но указывать имя модели при создании запросов является хорошей практикой. Это делает код более понятным и помогает предотвратить конфликтные ситуации в будущем, если вы решите внести изменения в вашу схему данных.

Что касается других подобных запросов, то они также просты. Допустим, мы хотим найти все посты, в которых название не равно «Это пост»:

<? array("Post.title <>" => "This is a post"?>

Заметьте, что '<>' следует за именем поля. CakePHP может разобрать любой допустимый SQL оператор сравнения, включая выражения, использующие LIKE, BETWEEN, или REGEX, только если вы поставите пробел между именем поля и оператором. Единственное исключение – это оператор IN (...). Например, вы хотете найти все посты с названиями из заданного множества:

<?

array(

    "Post.title" => array("Первый пост""Второй пост""Третиий пост")

)

?>

Чтобы сделать NOT IN(...) аналог, для поиска постов с названиями, не входящими в данное множество:

<?

array(

    "NOT" => array( "Post.title" => array("Первый пост""Второй пост""Третиий пост") )

)

?>

Добавить дополнительный фильтр в условие также просто, как добавить пару индекс/значение в массив:

<?

array (

    "Post.title" => array("Первый пост""Второй пост""Третиий пост"),

    "Post.created >" => date('Y-m-d'strtotime("-2 weeks"))

)

?>

Вы также можете создавать поисковые запросы, которые сравнивают два поля в базе данных:

<? array("Post.created = Post.modified"?>

В примере выше, будут возвращены посты, у которых дата создания равна дате модификации (т.е. посты, которые никогда не редактировались).

Помните, что если вы не можете сформировать WHERE условие, то можете определить его в виде строки, например:

<?

array(

    'Model.field & 8 = 1',

    //другие условия как обычно

)

?>

По умолчанияю CakePHP соединяет множественные условия с помощью оператора AND; что для примера выше означает – будут выбраны посты, созданные в последние две недели и имеющие любое название из данного множества. Однако, мы можем также легко найти посты, которые соответствуют условию OR:

<?

array( "or" => array (

    "Post.title" => array("Первый пост""Второй пост""Третиий пост"),

    "Post.created >" => date('Y-m-d'strtotime("-2 weeks"))

    )

)

?>

Кейк принимает все действующие логические SQL-операторы, включая AND, OR, NOT, XOR, и др. Они могут быть набраны строчными или заглавными буквами. Эти условия также бесконечно «гнездящиеся». Предположим, у вас есть belongsTo ассоциация между Posts и Authors. И вы хотите найти все посты, содержащие конкретное слово(«волшебный») или, которые были созданы в последние две недели, но вы хотите ограничить ваш поиск только постами, написанными Василием:

<?

array (

    "Author.name" => "Василий"

    "or" => array (

        "Post.title LIKE" => "%волшебный%",

        "Post.created >" => date('Y-m-d'strtotime("-2 weeks"))

    )

)

?>

Кейк может также проверить поля на null. В этом примере запрос вернет записи, в которых заголовок поста не null:

<?

array ("not" => array (

        "Post.title" => null,

    )

)

?>

Для выполнения BETWEEN запросов, вы должны использовать следующее:

<? array('Post.id BETWEEN ? AND ?' => array(1,10)) ?>

Внимание: CakePHP будет брать в кавычки численные значения, в зависимости от типа поля в БД.

А как же с GROUP BY?

<? 

array('fields'=>array('Product.type','MIN(Product.price) as price'), 'group' => 'Product.type'); 

?>

Быстрый пример создания DISTINCT запроса. Вы можете использовать другие операторы, такие как MIN(), MAX(), и пр., в подобном виде:

<? 

array('fields'=>array('DISTINCT (User.name) AS my_column_name'), 'order'=>array('User.id DESC'));

?>

Вы можете создавать очень сложные условия:

<?

array(

   'OR' => array(

      array('Company.name' => 'Future Holdings'),

      array('Company.name' => 'Steel Mega Works')

   ),

   'AND' => array(

      array(

         'OR'=>array(

            array('Company.status' => 'active'),

            'NOT'=>array(

               array('Company.status'=> array('inactive''suspended'))

            )

         )

     )

   )

);

?>

Условие выше сгенерирует следующий SQL:

SELECT `Company`.`id`, `Company`.`name`, 

`Company`.`description`, `Company`.`location`, 

`Company`.`created`, `Company`.`status`, `Company`.`size`

FROM

   `companies` AS `Company`

WHERE

   ((`Company`.`name` = 'Future Holdings')

   OR

   (`Company`.`name` = 'Steel Mega Works'))

AND

   ((`Company`.`status` = 'active')

   OR (NOT (`Company`.`status` IN ('inactive', 'suspended'))))

<< Создание таблиц базы данных | Сохранение данных >>