📜 ⬆️ ⬇️

Screw ActiveRecord to the site

Introduction

In the process of creating more than a complex site, you have to think about organizing access to the database (database). If the site is created on the basis of an existing framework or CMS, then there are usually built-in ORM mechanisms (from English - Object-relational mapping, more details in the wiki ). In this article I will tell you how to screw the popular and simple ORM system ActiveRecord to your own framework.

How does ActiveRecord work?

The component is a set of basic classes necessary for work (Model, Config, ConnectionManager, etc.), a set of adapters for connecting to a specific DBMS and entry point, an ActiveRecord.php initialization file that contains the autoloading function of the classes of our project models. All classes are defined in the ActiveRecord namespace, our project will most likely be in a different space or in the global one, so that when class is inherited, not to write constructions like extends \ ActiveRecord \ Model every time or use the use directive Use ActiveRecord, it makes sense to create your own wrapper over ActiveRecord. It will also expand the capabilities of our ORM without affecting the AR component.

So, to use all the AR methods, we need to connect the ActiveRecord.php initialization file to the project, create a class-model for each table in the database and inherit it from \ ActiveRecord \ Model (for example, Book extends \ ActiveRecord \ Model {}), initialize connection to the database using the design:

$connections = array( 'development' => 'mysql://invalid', 'production' => 'mysql://test:test@127.0.0.1/test' ); ActiveRecord\Config::initialize(function($cfg) use ($connections) { $cfg->set_model_directory('.'); $cfg->set_connections($connections); }); 

')
After that we can call our models and call the necessary methods, for example Book :: first () - returns the first row from the table defined in the Book model.

Creating AR Wrapper

The project may need to access the database from different files, and the configuration is usually stored in a separate file, the standard AR capabilities are not always sufficient and the form itself through the namespace \ ActiveRecord is not very beautiful. This topic draws on several articles, so here I will try to explain the essence of the issue.

In the simple case, we need to create only 2 classes, one we inherit from \ ActiveRecord \ Model and the other will be the main one in which we will carry out the initialization and configuration of AR. Create 2 class files:

 //Orm.php class Orm { /** * array $models_    ,          ,     *     [ ]=>array('path'=>       , 'namespace'=>       ) */ public $models_ = array(); /** *    PHP,   ,    ,   * * @param null $name */ function __construct($name = null) { if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) die('PHP ActiveRecord requires PHP 5.3 or higher'); define('PHP_ACTIVERECORD_VERSION_ID', '1.0'); include_once 'lib/Singleton.php'; include_once 'lib/Config.php'; include_once 'lib/Utils.php'; include_once 'lib/DateTime.php'; include_once 'lib/Model.php'; include_once 'lib/Table.php'; include_once 'lib/ConnectionManager.php'; include_once 'lib/Connection.php'; include_once 'lib/SQLBuilder.php'; include_once 'lib/Reflections.php'; include_once 'lib/Inflector.php'; include_once 'lib/CallBack.php'; include_once 'lib/Exceptions.php'; spl_autoload_register(__NAMESPACE__ . '\ActiveRecord::activerecord_autoload'); Config::initialize(function ($cfg) { $cfg->set_connections(array( 'development' => Configuration::$dbtype . "://" . Configuration::$db_user . ":" . Configuration::$db_password . "@" . Configuration::$db_host . "/" . Configuration::$db_name )); /*     ,       AR,    "Ymd H:i:s T"      ,         datetime  MySQL */ $cfg->set_date_format("Ymd H:i:s"); }); } /**      ,     ,      FALSE */ public function getModel($model) { $config = Config::instance(); if (array_key_exists($model, $this->models_)) { $config->set_model_directory($this->models_[$model]['path']); if( $this->models_[$model]['namespace'] ) $class = "\\" . $this->models_[$model]['namespace'] . "\\" . $model; else $class = $model; return new $class; } else { return false; } } /**   ,       getModel() $class_name   ,      NEW   getModel() */ public static function activerecord_autoload($class_name) { $root = Config::instance()->get_model_directory(); $class_name = explode('\\', $class_name); $class_name = end($class_name); $file = $root . $class_name . ".php"; if (file_exists($file)) require $file; } } //Model.php class Model extends \ActiveRecord\Model { /*    ,      ,         */ static $table_name = 'simple_name'; //      static $primary_key = 'id'; //      static $connection = 'production'; //    ,   SQL    - db.table_name static $db = 'test'; /* *            */ } 


From the Model class, we will inherit all models of existing tables. Also suppose that the entire application configuration is stored in a separate Configuration.php file:

 class Configuration{ /*.....*/ /** * $db_host       */ static $db_host = 'localhost'; /** * $db_user    */ static $db_user = 'root'; /** * $db_password    */ static $db_password = 'root'; /** * $db_name    */ static $db_name = 'db_name'; /** * $dbtype     */ static $dbtype = 'mysql'; /*.....*/ } 


In the Orm class constructor (this code is taken from ActiveRecord.php) we connect the necessary classes and register the autoloader, at the very end we initialize the connection to the database.

Particular attention should be paid to the time format, if you leave it by default, then during data write operations in the database, datetime fields will generate an error, since AR generates lines in the format 2000-02-03 16:23:27 MSK, i.e. indicates the index of the time zone. Changing the config is not enough, I don’t know why, but AR developers in other classes use the date and time format not from the config, but clearly indicate it in the required methods, so you will have to make more changes to the following files:
/lib/Column.php cast method
 return new DateTime($value->format('Ymd H:i:s T')) 

on
 return new DateTime($value->format(Config::instance()->get_date_format())) 

Similarly, in the files /lib/Connection.php methods datetime_to_string () string_to_datetime (), and /lib/Model.php method assign_attribute ().

Now I will give an example of how to use it all. First we need to create a variable in which we will store the object of our Orm class, this variable should be available in any place we need in any script, so it is better to declare it as a static main controller or global. After creating the object, it is necessary to place an array of all models used in the project in the _models array; the format of the array can be found in the comments in the code. Here is a possible example of the implementation of all this:

 <?php class Controller{ public static $ORM; function __construct(){ $this->loadOrm(); } function loadOrm(){ include 'Orm.php' self::$ORM = new Orm(); self::_models = array('Book'=>array('path'=>'models', 'namespace'=>__NAMESPACE__)); } } new Controller; ?> //            <?php $model = Controller::$ORM ->getModel('Book'); $books = $model->all(); foreach($books as $book) echo $book->author; 


Of course, this method requires further refinement, for example, you can make static methods of the Orm class, then when you start the project, we will need to initialize it, and then use a structure like Orm :: getModel ('Model Name') everywhere;
AR is quite a powerful and flexible tool, it supports, in addition to standard CRUD operations, also relationships between tables (including complex cross-through relationships), there is a SQLBuilder for building SQL queries, validation, conversion, etc.

Official documentation in English and in it elementary questions are covered, there is also a forum where you can find most answers on working with AR, but I could not google more than a normal source with information about implementing AR into my own framework or a simple site engine.

In the course of my work, I came across a dense stalk with this library, and if this topic is interesting, then I will continue this cycle of articles on ActiveRecord.

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


All Articles