====== Создание, сохранение, загрузка и удаление объектов ====== [[wp>ActiveRecord]] - это посредник между клиентским кодом и одной записью одной таблицы базы данных. ===== Таблицы базы данных ===== В процессе описания, мы будем опираться на класс User и таблицу user, с которыми мы работали в примере на странице [[intro|"Краткое описание"]]. Таблица **user** содержит автоинкрементное поле **id**. Это суррогатный первичный ключ. Его наличие обязательно во всех таблицах, с которыми вы работаете через класс lmbActiveRecord. **%%lmbActiveRecord%%** пытается самостоятельно определить, с какой таблицей стоит работать по названию класса. Если мы создали дочерний класс **%%BasePlan%%**, тогда по-умолчанию этот класс будет работать с таблицей base_plan. То есть название класса преобразуется из [[wp>CamelCase]] в under_scores. Вы можете самостоятельно задать имя таблицы, с которой следует работать дочернему классу посредством определения атрибута **$db_table_name** : class User extends lmbActiveRecord { protected $_db_table_name = 'my_user'; } lmbActiveRecord использует в своей работе класс [[limb3:ru:packages:dbal:lmb_table_gateway|lmbTableGateway]], который обеспечивает все низкоуровневневые операции по считыванию и записи данных из соответствующей таблицы базы данных. Также lmbTableGateway считывает информацию о структуре таблицы, о типах полей и обеспечивает фильтрацию полей и type castring, когда это необходимо. ===== Создание и заполнение объектов ===== Можно заполнять объекты по одному полю, например: $user = new User(); $user->setName('Vasa'); $user->setLastName('Pupkin'); $user->getName(); // Выведет Vasa $user->getLastName(); // Выведет Pupkin или так: $user = new User(); $user->set('name', 'Vasa'); $user->set('last_name', 'Pupkin'); $user->get('name'); // Выведет Vasa $user->get('last_name'); // Выведет Pupkin Так как lmbActiveRecord наследуется от [[limb3:ru:packages:core:lmb_object|lmbObject]], он поддерживает магические getter-ы и setter-ы, например, getName() и setName() даже если этих методов нет. Также можно передавать массив данных прямо в конструктор: $data = array('name' => 'Vasa', 'last_name' => 'Pupkin'); $user = new User($data); Для заполнения и получения данных из объектов можно также использовать методы **import()** и **export()**. В качестве агрумента метода **import()** можно использовать массив, другой объект [[wp>ActiveRecord]] или любой иной контейнер данных, который поддерживает метод export(), например, запись из базы данных или же объект класса lmbSet, например: $user1 = new User(); $user1->import(array('name' => 'Vasa', 'last_name' => 'Pupkin')); $user2 = new User(); $user2->import(lmbSet(array('name' => 'Vasa', 'last_name' => 'Pupkin'))); $other_user = new User(array('name' => 'Vasa', 'last_name' => 'Pupkin')); $user3 = new User(); $user3->import($other_user); ===== Сохранение объектов ===== Метод lmbActiveRecord :: **save()** создает в таблице новую запись, если объект является новым. По-умолчанию объект является новым, если у него еще нет идентификатора (значения в поле **id**) . Если объект уже существует, когда при вызове save() запись будет обновлена. Метод lmbActiveRecord :: save() возвращает идентификатор созданной или измененной записи. $user = new User(); $user->setName('Vasa'); $user->save(); // Вернет сгенерированный идентификатор. Для получения информации, является ли объект новым, можно использовать метод lmbActiveRecord :: **isNew()**. Метод lmbActiveRecord :: **isDirty()** возвращает true, если объект содержит данные, которых еще нет в базе данных (новые или изменение в процессе работы после загрузки). При помощи метода lmbActiveRecord :: **isDirtyProperty($property_name)** можно узнать, была ли изменено определенное поле. Все это лучше продемонстрировать небольшим куском кода: $user = new User(); $user->setName('Vasa'); $user->isNew(); // Вернет true $user->isDirty(); // Вернет true $user->save(); $user->isDirty(); // Вернет false $user->isNew(); // Вернет false $user->setName('Ivan'); $user->isNew(); // Вернет false $user->isDirty(); // Вернет true $user->save(); $user->isDirty(); // Вернет false ===== Загрузка объектов ===== Для загрузки ранее сохраненного объекта по имеющемуся идентификатору можно использовать передачу этого идентификатора в конструктор, например: $user = new User($user_id); Если запись с таким идентификатором не будет найдена, будет сгенерировано исключение класса **lmbARNotFoundException**. Например: try{ $user = new User(-100); } catch(lmbARException $e){ addError('Cant load object!'); } Также можно использовать статический метод lmbActiveRecord :: **findById($class_name, $id)**: $user = lmbActiveRecord :: findById('User', $user_id); Если запись с указанным идентификатором не будет найдена, также будет сгенерировано исключение класса lmbARNotFoundException, как и в случае передачи идентификатора в конструктор. Если вы не желаете получать исключения, тогда используейте третий параметр $throw_exception = false, например: $user = lmbActiveRecord :: findById('User', $user_id, $throw_exception = false); К сожалению, в PHP пока практически невозможно сделать нечто подобное $user = User :: findById($user_id) (то есть получить именно объект класса User) без применения средств автогенерации или ручного добавления метода findById в класс User. Поэтому приходится писать именно lmbActiveRecord :: findById('User'...); Третий вариант загрузки метод lmbActiveRecord :: loadById($id), например: $user = new User(); $user->loadById($user_id); Наше личное мнение заключается в том, что нужно все же использовать статический метод findById вместо loadById(тем более, что внутри loadById реализован через findById). Если вам нужно загрузить объект по какому-либо условию, можно воспользоваться методом lmbActiveRecord :: **findFirst($class_name, $params = array())**. В качестве аттрибута второго параметра criteria можно передавать чаcть SQL-запроса или объект из набора [[limb3:ru:packages:dbal:criteria|Criteria]]. Также можно задать правила сортировки, используя аттрибут sort, передав ассоциативный массив где ключ - поле, а значение - способ сортировки. Например: $criteria = new lmbSQLFieldCriteria('name', '%Vasa%', lmbSQLFieldCriteria :: LIKE); $sort = array('name' => 'DESC', 'last_name' => 'ASC'); $user = lmbActiveRecord :: findFirst('User', array('criteria' => $criteria, 'sort' => $sort)); Универсальный статический метод lmbActiveRecord :: **find($class, $params = array())** используется для загрузки сразу нескольких объектов с возможностью применения сортировки и фильтрации по некоторому условию. Метод find() возвращает итератор c объектами. То есть, с каждым элементов можно работать, как с полноценным объектом класса lmbActiveRecord. Более того, этот итератор поддерживает интерфейс [[limb3:ru:packages:core:lmb_collection_interface|lmbCollectionInterface]]. Подробнее о различных find()-методах будет рассказано в соответствующем разделе [[find|"Поиск и сортировка объектов"]]. ===== Удаление ===== Для удаления записи из таблицы используется метод lmbActiveRecord :: **destroy()**. Этот метод используется если объект уже был загружен или же он имеет идентификатор, например, проставленный вручную, например: $user = new User($user_id); $user->destroy(); $user = new User(); $user->setId($user_id); $user->destroy(); Для удаления нескольких объектов используется статический метод lmbActiveRecord :: **delete($class, $params = array())**. В качестве аттрибута criteria второго аргумента можно передавать часть SQL-запроса, или же объект одного из классов [[limb3:ru:packages:dbal:criteria|Criteria]]. Например: lmbActiveRecord :: delete('User', 'id=100'); lmbActiveRecord :: delete('User', new lmbSQLFieldCriteria('name', '%Vasa%', lmbSQLFieldCriteria :: LIKE)); Если критерий не задан, то будут удалены все объекты заданного класса. lmbActiveRecord :: delete('User'); //удалит все объекты User, загружая каждый объект в память. Обратите внимание, что при использовании метода delete() объекты не будут удалены одним DELETE запросом. Объекты будут загружены из базы данных при помощи find() метода и удалены по-одному. Это сделано для того, чтобы обеспечивать целостность данных при наличии какой-либо бизнес-логики при удалении и в случае, если ваши **ActiveRecord-ы** находятся в различных отношениях. Если вам необходимо обеспечивать быстрое удаление без дополнительных проверок, этот код нужно будет создавать вручную. ===== Изменение состояние таблицы напрямую без загрузки объектов ActiveRecord ===== Для удаления записей из соответствующей таблицы напрямую, без загрузки каждого объекта, можно использовать метод lmbActiveRecord :: **deleteRaw($class_name, $criteria = null)**, например: lmbActiveRecord :: deleteRaw('User'); //удалит все объекты User одним запросом Для обновления записей можно использовать метод lmbActiveRecord :: **updateRaw($class_name, $set, $criteria = null)**, например: lmbActiveRecord :: update('user', array('access' => 10), lmbSQLCriteria :: equal('login', 'vasa')); Обратите внимание, что в этом случае вся информация об отношениях или какая еще логика, связанная с удалением или обновлением (кроме той, что реализована в самой базе данных), будет игнорирована.