[table]В конце концов, PHP получит более надежный способ активной записи аналогичный способу применяемому в RoR. И, к счастью, это время настало! Спасибо PHP 5. 3 и его новым полезным функциям: схлопыванию, позднему статистическому связыванию и пространству имен. Я со своим другом Каеном, внесли улучшения в раннюю версию на базе ORM, которую он написал до появления PHP 5. 3. Мы создали ActiveRecord вдохновленные Ruby on Rails и попытались сохранить его возможности, насколько это было возможно. Нашей основной целью в этом проекте – дать возможность PHP-разработчикам создавать крупные проекты с большей гибкостью.
Мы также надеемся что использование данного продукта подтолкнет PHP сообщество к дальнейшему осознанию всех замечательных преимуществ Ruby on Rails. Ладно , хватит крутиться вокруг да около, давайте перейдем к самому интересному!
Обзор ActiveRecord
Позвольте мне согласиться с тем фактом, что мы пытались сохранить сходство между нашим детящем и ActiveRecord на базе Ruby on rails, чтобы избежать головной боли и повысить работоспособность программиста. Сохраняя это сходство, мы постарались воссоздать многие функции. Вот список этих функций:
* Методы поиска
* Методы динамического поиска
* Методы записи
* Отношения
* Верификация
* Обратные вызовы
* Сериализация
* Поддержка различных драйверов
* Другие параметры
Также здесь есть и другие возможности, как пространства имен, дополнительные драйверы, транзакции (то чего мы хотели бы пораньше) и многое другое мы добавим в будущем, но для начала я думаю совсем неплохо. Мы надеемся, запустить сайт с документацией и разместить исходники на сервисе исходных кодов в течении 2-3х недель. Не забывайте следить за обновлениями, которые вскоре появятся.
Конфигурация
Установка проста и линейна. Здесь есть всего 2 типа конфигурации, которые вы можете выбрать:
* Установка типовой auto_load директории
* Настройка соединений с вашей базой данных
Примеры:
ActiveRecord\Config::initialize(function($cfg)
{
$cfg->set_model_directory('/path/to/your/model_directory');
$cfg->set_connections(array('development' =>
'mysql://username:password@localhost/database_name'));
});
#Альтернативно(w/o ограничение 5.3):
$cfg = ActiveRecord\Config::instance();
$cfg->set_model_directory('/path/to/your/model_directory');
$cfg->set_connections(array('development' => 'mysql://username:password@localhost/database_name'));
Как только вы настроите два этих параметра ваша работа окончена. ActiveRecord заботится о вас и берет остальную часть работы на себя. Она не требует проводить какую либо дополнительную работу со схемами ваших yaml/xml файлов. Также запросы к базу данных для получения информации и кэш-файлов будет проходить без лишних вызовов для одной схемы.
Методы поиска
ActiveRecord поддерживает несколько методов, с помощью которых, вы можете найти записи либо по первичному ключу, либо сконструировав свой собственный с набором опций, к примеру: сортировка, лимит, выбор, группировка.
#поиск по первичному ключу
Author::find(3);
#поиск нескольких записей по первичному ключу
Author::find(1, 2, 3);
#поиск первой записи по лимиту
Author::first();
#поиск последней записи по сортировке и лимиту
Author::last();
Author::all();
#это возможно будет для вас адом, но вы должны выполнить свой sql-запрос для поиска
Author::find_by_sql();
## еще вы можете добавить много дополнительных опций к методам поиска
#sql => ORDER BY name
Author::find(3, array('order' => 'name'));
#sql => WHERE author_id IN (1, 2, 3)
# поиск с условиями как массив
Author::find('all', array('conditions' => array('author_id IN(?)', array(1, 2, 3))));
#sql => WHERE author_id = 3
#поиск с условиями как строка
Author::find('first', array('conditions' => 'author_id=3'));
#sql => GROUP BY name
Author::find(3, array('group' => 'name'));
#sql => LIMIT 0, 3
Author::find('all', array('limit' => 3));
#sql => select * from `author` INNER JOIN etc. . .
Author::find('all', array('joins' =>
'INNER JOIN book on (book. author_id = author. id)'));
#sql => SELECT name FROM table
Author::first(array('select' => 'name'));
#этод метод не возвращает запись
#возвращает да/нет
Author::exists(1);
#возвращает число
Author::count(array('conditions' => array('name = ?', 'John')));
#доступ очень простой
$book = Book::first();
echo $book->title;
echo $book->author_id;
# возвращает массив
$books = Book::all();
echo $books[0]->title;
Методы динамического поиска
ActiveRecord на основе RoR расширяет функции использования поиска, позволяя динамически создавать методы основанные на атрибутивных именах. Это значит, вы с легкостью можете сделать запрос"find_by_attribute_name" без явного классового определения. Мы сделали это возможным с помощью использования волшебного метода в PHP 5.3: __callStatic().
Author::find_by_name('George Bush');
#вы можете это сделать используя и/или
Author::find_by_name_or_author_id('George Bush', 2);
Person::find_by_first_name_and_last_name('Obama', 'Mama');
#также имеется find_all_by(найти всё по какому-либо параметру)
Author::find_all_by_name('Tito');
Author::find_all_by_name(array('Tito', 'Bill Clinton'));
Методы записи
Какой смысл иметь объект, который инкапсулирует запись из БД, если вы не можете ничего сделать с этим?
#выполняет SQL insert, такой же какой вы делаете при добавлении записи
$book = new Artist(array('name' => 'Tito'));
$book->save();
## обновление
# обновление возможно, только после нахождения обновляемой строки
$book = Book::find(1);
$book->title = 'new title!';
$book->save();
#это автоматически выполняет сохранение
$book = Book::find(1);
$book->update_attributes(array('title' => 'new title!',
'price' => 5. 00));
#еще автоматическое сохранение
$book = Book::find(1);
$book->update_attribute('title', 'some new title');
$book = new Book;
$book->title = 'new title!';
$book->author_id = 5;
$book->save();
$book->created_at # мы также поддерживаем временные отметки created_at/updated_at где это нужно
echo $book->id; #id также получает авто инкрементированное значение
#удаление
$author = Author::find(4);
$author->delete();
#вы получаете запись в режими только чтение без возможности изменить её
$book = Book::first(array('readonly' => true));
$book->title = 'new'; # или вы можете установить этот режим с помощью метода $book->readonly(true);
#это запустит процессы ActiveRecord\ReadonlyException
$book->save();
Отношения
Объединения - сложная часть ActiveRecord. Они используют те же самые опции, что в RoR.
#отношения декларируются в статическом свойстве
class Book extends ActiveRecord\Model {
static $belongs_to = array(
array('publisher'),
array('author', 'readonly' => true, 'select' => 'name', 'conditions' => "name != 'jax'"),
array('another', 'class_name' => 'SomeModel')
);
#has_many допускает select/conditions/limit/readonly/group/primary_key
#has_many также применима через опцию которую вы можете использовать с источником(source)
#для уточнения класса
static $has_many = array(
array('revisions'),
array('editors', 'through' => 'revisions')
);
static $has_one = array(
array('something')
);
}
#индекс декларируемый для каждой ассоциации является "attribute_name"
#которую вы можете использовать для работы с моделью как, например:
$book = Book::first();
echo $book->publisher->name;
echo $book->author->name; # у нас есть только имя как атрибут выборки
#ниже будет запускаться опция readonlyException — смотрите методы записи
#методы выбора
$book->author->save();
#has_many
echo $book->revisions[0]->id;
#has_many
echo $book->editors[0]->name;
Верификация
Название говорит само за себя. До того как сохранить/обновить/извлечь к каждому определению, которое вы создали, будет применена верификация и только после её удачного прохождения модель будет возвращена. В противном случае, вы увидите сообщение об ошибке и сможете вернуться обратно для ее исправления.
class Book extends ActiveRecord\Model
{
static $validates_format_of = array(
array('title', 'with' => '/^[a-zW]*$/', 'allow_blank' => true)
);
static $validates_exclusion_of = array(
array('title', 'in' => array('blah', 'alpha', 'bravo'))
);
static $validates_inclusion_of = array(
array('title', 'within' => array('tragedy of dubya', 'sharks wit laserz'))
);
static $validates_length_of = array(
array('title', 'within' => array(1, 5)),
array('attribute2', 'in' => array(1, 2)),
array('attribute3', 'is' => 4, 'allow_null' => true)
);
# same as above since it is just an alias
static $validates_size_of = array();
static $validates_numericality_of = array( array('title') );
static $validates_presence_of = array( array('title') );
};
$book = new Book;
$book->title = 'this is not part of the inclusion';
if (!$book->save())
print_r($book->errors->on('title'));
Обратные вызовы
Обратные вызовы дают вам возможность управлять вашей моделью до/после какого-то события на протяжении ее существования. Вы можете выбрать методы, которые будут вызваны как callback до или после других методов примененных в модели. К сожалению, даже PHP 5.3 имеет ограничения - вы не можете иcпользовать callback в виде статических методов, они должны быть динамическими.
#Ниже приведены команды, которые вы можете использовать
#Если ваш обратный вызов является недействительным для before_*, он отменит
#действие и остальные вызовы
class Book extends ActiveRecordModel{
static $after_construct;
static $before_save = array('do_something_before_save');
static $after_save;
static $before_create;
static $after_create;
static $before_update;
static $after_update;
static $before_validation;
static $after_validation;
static $before_validation_on_create;
static $after_validation_on_create;
static $before_validation_on_update;
static $after_validation_on_update;
static $before_destroy;
static $after_destroy;
#это будет вызвано перед обновлением
public function do_something_before_save() {}
}
Сериализация
class Book extends ActiveRecord\Model{
public function upper_title(){
return strtoupper($this->title);
}
}
#produces: {title: 'sharks wit lazers', author_id: 2}
$book = Book::find(1);
$book->to_json();
#produces: {title: 'sharks wit lazers'}
$book->to_json(array('except' => 'author_id'));
#produces: {upper_title: 'SHARKS WIT LAZERS'}
#создает массив методов, которые затем будут вызваны
$book->to_json(array('methods' => 'upper_title', 'only' => 'upper_title'));
#produces {title: 'sharks wit lazers', author_id: 2}
$book->to_json(array('include' => array('author')));
#также поддерживает xml, который имеет теже опиции, но нужны больше для тестирования =)
$book->to_xml();
Поддержка различных драйверов
В настоящее время существует поддержка только для MySQL (через mysqli) и sqlite3. Однако мы надеемся ввести большее число драйверов в проект, что должно помочь в работе с такими БД как PostgresSQL и Oracle. Соедиение/адаптер - сделан так, что не возникнет никаких затруднений при создании большего числа драйверов, когда будет нужно.
Другие параметры
При декларировании модели вы также можете указать primary_key и table_name.
Защищенные/доступные определения уже доступны, так что вы можете справится со многими распространенными проблемами. Атрибуты могут быть псевдонимами, чтобы вам было легче получить доступ к ним через разные имена.
class Book extends ActiveRecord\Model{
static $primary_key = 'book_id';
static $table_name = 'book_table';
static $attr_accessible = array('author_id');
static $attr_protected = array('book_id');
static $alias_attribute = array(
'new_alias' => 'actual_attribute',
'new_alias_two' => 'other_actual_attribute'
);
}
Будущее
Как я уже отмечал ранее, в самое ближайшее время данный код будет доступен на сервисах исходного кода. Мы также работаем над созданием сайта с учебными пособиями и документациями к коду. Я буду сообщать о новых успехах.
Спасибо за прочтение!
UPD: ActiveRecord доступен по адресу http://github.com/kla/php-activerecord/tree/master
Данная статья является переводом PHP ActiveRecord with PHP 5.3.
Подготовленно командой Internet-Technologies.Ru[/table]