📜 ⬆️ ⬇️

New features in the framework repository: ORM / ActiveRecord

image
I would like to start a series of articles on the development of the LiveStreet engine, namely its framework part. LiveStreet has become quite popular as a blog social blog , but in 2 years it has grown into something much larger. Especially with the release of version 0.4. *, When there were ample opportunities for writing plug-ins with the functionality of inheritance and delegation .
Quite a large number of large social networks have already been built on LiveStreet using these technologies.

In this regard, developers need to develop functionality, modules and hacks for their projects. The introduction of plug-ins has simplified this process at times. We continue to work in this direction: now I will talk about the alpha version of the implementation of the ORM approach based on the ActiveRecord pattern, which we developed (and continue to develop) in LiveStreet.


')
image
What is ORM (in a nutshell)

The idea of ​​ORM lies in the presentation, mapping of database tables as classes (models), and records from them as objects of this class. The main goal of the ORM approach is to automate standard routine functions ( CRUD ) for working with data in the database, eliminate the unnecessary need to write SQL queries and generate responses.

As it was before

Previously, when creating a model ( Sample ), it was necessary to perform a series of uniform actions and write or copy a good amount of code. Only according to the LS structure, it was necessary to create 3 large files:
/ modules/ sample/Sample.class.php - module file: a set of functions for managing models: AddSample (), UpdateSample (), GetSampleById (), GetSampleByBlablabla () , etc., most of which elicit a synonym from mapper (see below) and handles caching.
/modules/sample/entity/Sample.entity.class.php - the essence of the model, has a list of methods a la getId (), getBlablabla (), setId (), setBlablabla () ;
/modules/sample/mapper/Sample.mapper.class.php is a sql-mapper of the model, which contains almost the same functions as in the module file, only with sql queries to the tables.
And all these methods had to be registered / rewritten manually each time a new model was created. In addition, it was necessary to prescribe additional settings, such as the name of the table in the database, etc.

What can be done now

What does the ORM implementation give us? In fact, the opportunity will get rid of ~ 80% of the uniform code .
Modules, entities and mappers that inherit the corresponding ORM classes automatically possess most of the standard methods: get * (), Add (), Save (), GetBy * (), GetItemsBy * (), Delete () , etc.

I will give a more detailed, classic example. Suppose we want to create a “photo album” module.
1) First, create the “Album” prefix_album and “Photo” prefix_photo entity tables in the database of the form:
album_id | author_id | album_title 
and
 photo_id | album_id | photo_title | photo_img_src 


2) Create a module file of our gallery: /classes/modules/gallery/Gallery.class.php :
 <?php class ModuleGallery extends ModuleORM { public function Init() { parent::Init(); } } ?> 


3) Create the entities files /classes/modules/gallery/entity/Album.entity.class.php and /classes/modules/gallery/entity/Photo.entity.class.php :
 <?php class ModuleGallery_EntityAlbum extends EntityORM {} ?> 
and
 <?php class ModuleGallery_EntityPhoto extends EntityORM {} ?> 


4) And ... that's all. Now we can manage our entities through singleton Engine:
 //   : $oUserCurrent = Engine::GetInstance()->User_GetUserCurrent(); //  short-alias: $oUserCurrent = LS::CurUsr(); //   : $oAlbum = Engine::GetEntity('ModuleGallery_EntityAlbum'); //  short-alias: $oAlbum = LS::Ent('Gallery_Album'); //    $oAlbum->setAutorId($oUserCurrent->getId()); $oAlbum->setTitle('First Album'); //     `prefix_album` $oAlbum->Add(); //  $oAlbum->Save(); //    : $oAlbum = Engine::GetInstance()->Gallery_GetAlbumByTitle('First Album'); //  short-alias: $oAlbum = LS::E()->Gallery_GetAlbumByTitle('First Album'); //   (  "Items"): $aPhotos = LS::E()->Gallery_GetPhotoItemsByAlbumId($oAlbum->getId()); //    : $oPhoto = LS::E()->Gallery_GetPhotoByTitleAndAlbumId('',$oAlbum->getId()); //  : foreach($aPhotos as $oPhoto) { $oPhoto->setAlbumId(2); $oPhoto->Save(); } //  .... 


According to our estimates, even this small set is enough to implement 30% -40% of the methods (read the lines of code) described in the standard modules, entities and mapers by the old method.
I think ... it's great! :)

What else? ... Relationships

An important element in any web application is connections (relationships) of models. Typically, these relationships are created through the database using primary keys in the tables.
Let's go back to our example: it’s obvious that the photos in the gallery are related to the album, and therefore I don’t know how you are, but I think it would be cool to
 //   (  "Items"): $aPhotos = LS::E()->Gallery_GetPhotoItemsByAlbumId($oAlbum->getId()); 

we could just write ...
 $aPhotos = $oAlbum->getPhotos(); 

Yes, it would definitely be cooler! No problem :) To bind entities you just need to configure their relationships using the $ aRelations array in the entity class description.
Let's return to our currently empty file /classes/modules/gallery/entity/Album.entity.class.php and add the following code to it:
 <?php class ModuleGallery_EntityAlbum extends EntityORM { protected $aRelations = array( 'photos' => array(self::RELATION_TYPE_HAS_MANY,'ModuleGallery_EntityPhoto','album_id'), 'author' => array(self::RELATION_TYPE_BELONGS_TO,'ModuleUser_EntityUser','author_id'), //  : 'photos' => array('has_many','Gallery_Photo','album_id'), 'author' => array('belongs_to','User','author_id'), ); } ?> 

And for the file /classes/modules/gallery/entity/Photo.entity.class.php we write this:
 <?php class ModuleGallery_EntityPhoto extends EntityORM { protected $aRelations = array( 'album' => array('belongs_to','Gallery_Album','album_id'), ); } ?> 

And that's all. Now we can even more easily operate with entities, attention :
 $aPhotos = $oAlbum->getPhotos(); $sUserLogin = $aPhotos[0]->getAlbum()->getAuthor()->getLogin(); 


We will understand the syntax of the $ aRelations array .
Its keys are the names of the related entities, which will then be accessible via get * . Values ​​in most cases are also arrays consisting of the following elements:

What's next?

For the first time, I think it’s enough for now, in the next article I’ll dwell on the description of relationship types, I’ll tell you about the type of tree , which allows you to manage tree structures; about the possibilities of automatic loading of related entities in GetBy * () ; as well as additional methods such as GetByFilter (), GetCountItemsBy * (), GetByArray * (), Update (), Delete (), Reload (), reload * () and others.

Why is this all?

We call the developers. Despite a successful start, the community framework is now almost in its childhood, and we understand perfectly well that without good people, without a real team, it will be much more difficult for us to bring LiveStreet to an international level. Yes, and we strive for this :)
I sincerely hope that our innovations will help developers (as well as all the rest: users and startups, copywriters and PR managers, specialists and investors) to become interested in the truly wonderful, young and promising LiveStreet engine.

And, finally, I would like to ask in advance not to make holivars on the topic “And have RoR / ZF / Yii ActiveRecord in a different way” and “don't worry, put Doctrine for yourself!”, Etc. Let's be optimistic.

Thanks for attention.
Respectfully,
Alexander Zinchuk (Ajaxy).

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


All Articles