📜 ⬆️ ⬇️

CMS architecture. Data model Part 3

In the previous article, on the example of creating an object model of a simple site, single entities were loaded from the database by their identifiers using the Object :: Create ($ id) construct, and we knew which entity (most often the class) had the identifier, because created these entities and in extreme cases could simply look into the database. In practice, it is problematic to load entities by identifier if we are interested in entities whose existence can only be guessed, that is, without having information about their identifiers. Moreover, there is a need to load several entities at once, meeting certain conditions.

In a store, for example, we do not select a product by its serial number or bar-code, without knowing what it means - we are looking at the properties of the product that interest us. On the main page of the site, again, for example, it is necessary to display the latest news, which comes down to a selection of 10 objects of the News class from the database (data object model) sorted by the date they were created. To make such requests, you need a flexible way to describe the conditions for the selection of entities - the search conditions with regard to the features of the object model. On the basis of the condition, it is necessary to create SQL code for direct sampling from the database of identifiers of entities that satisfy the condition, we have identifiers - we have entities.

To create a search condition, an object approach is used. From objects representing logical and other functions, a hierarchy is created; theoretically, it can be interpreted into a similarity to a mathematical function. Below is an example of a condition for getting “news” class entities. It is deciphered as follows: select entities (Q :: Entity) belonging to a class (Q :: IsClass) whose attribute 'sys_name' (Q :: Attrib) is equal to the value 'news' (Q :: Comp).
')
$cond = Q::Entity( Q::IsClass( Q::Comp(Q::Attrib('sys_name'), '=', 'news') ) ); 

The condition is used to create a Query object. The Query object interprets the condition in SQL code and executes it when the Execute () method is called. The result of the query is an array of found entities, or if the condition began with the Count function, the result will be an integer - the number of entities that satisfy the condition. Search for entities can be limited in number and perform an offset as is done with the LIMIT parameter in SQL, the limitation is performed by the second and third arguments when creating a query, or by using the SetCount () and SetStart () methods of the query object.

 //     $cond       10 //    (0)  $q = new Query($cond, 10, 0); //  .  –    Object $list = $q->Execute(); if (sizeof($list)>0){ echo $list[0]->getP('head')->getA('value'); //    } 

A condition is created from objects of the CondAttrib, CondClass, CondComp, CondCount, CondEntity, CondIsExist, CondLink, CondLog, CondNotExist, and CondParam classes, but in order to simplify the syntax instead of directly using the new operator and the Cond * classes, the static class Q is used. With its static methods ( factory methods) condition objects are created.

The condition includes arguments (entities, attributes, relationships), functions for checking the presence / absence of attributes or properties of an entity, a function for counting the number of properties of an entity or entities themselves, logical functions Or and And that also serve as parentheses, and a function for comparing attribute values or the result of the counting function using the operations '=', '>', '<', '<=', '> =', '<>', 'like'.

Unlike SQL, when sampling it is not necessary to specify where to choose (from which tables), no merging and grouping of results are needed either. In the request with the help of the condition it is simply indicated WHAT to choose. Since absolutely everything is an object in the object model, there is nothing to choose except objects, the only thing you can do is find out the number of objects satisfying the condition instead of searching for objects. Therefore, a condition always begins with an argument of an entity or entity counting function.

 // -   $cond1 = Q::Entity( ); // -   $cond2 = Q::Count(Q::Entity( )); 

In the first variant, an array with absolutely all entities of the object model will be returned (all objects, classes and relations), of course, if there are no restrictions on the number of the required entities in the Query query. In the second case, the number will be returned - the total number of entities.

We are rarely interested in absolutely all entities, so the argument condition of the Q :: Entity ($ cond) entity is supplemented with the $ cond condition, it can also be called a filter. It defines which attributes and properties should be or be absent from the sought-for entities, as if continuing the condition: “Entities with which ...”

Attributes


The condition can be put on the value of the attribute of the entity. To do this, use the argument condition denoting the attribute Q :: Attrib and the comparison function Q :: Comp. The following condition means: “Entities whose sys_name attribute is equal to the link value”

 $cond = Q::Entity( Q::Comp(Q::Attrib('sys_name'),'=','link') ); 

A condition can be supplemented with logical AND and OR functions (Q :: LogAnd and Q :: LogOr), if the test is for a value or a general condition for an entity is ambiguous. The logical functions "AND" and "OR" also play the role of brackets, with the help of which you can create complex conditions of any nesting.

A query with the following condition will return entities whose sys_name attribute is equal to the link value or if the sys_name attribute is equal to the label value and the is_define attribute is 0.

 $cond = Q::Entity( Q::LogOr( Q::Comp(Q::Attrib('sys_name'),'=','link'), Q::LogAnd( Q::Comp(Q::Attrib('sys_name'),'=','label'), Q::Comp(Q::Attrib('is_define'),'=',0) ) ) ); 

In the object model, the creation of which was discussed in the previous article, the sys_name attribute is in classes and links, so the result of the above condition will be the class link and links to system label names that do not define properties. The result will be different types of entities. Although the given condition is hardly practical, it demonstrates well the indifference to the types of entities in the search, note that the condition does not specify the existence of attributes whose values ​​are compared. The search is performed in almost the same way as a person. For example, to find everything that is red among different types of objects: a lantern, a sunset, a tractor, a ball, blood, milk, the flag of the CCSR :) - the search is carried out without problems, the main thing is that the red ones are.

Parameter instead of value


In the comparison function Q :: Comp, instead of a scalar value with which the attribute is compared, you can use the parameter Q :: Param, which allows you to later set the value of the parameter when the query is generated. Thus, you can use one query several times with the possibility of reassigning the values ​​of its parameters.

 $cond = Q::Entity( Q::Comp(Q::Attrib('sys_name'),'=', Q::Param('par1')) ); //   $q = new Query($cond); //    $q->SetValue('par1', 'news'); //   $list1 = $q->Execute(); //     $q->SetValue('par1', 'name'); //   $list2 = $q->Execute(); 

In addition to comparing the values ​​of attributes, you can simply set the condition for their presence by the function-condition Q :: IsExist (). The condition below defines entities that have a value attribute, the result will be all objects of strings and numbers.

 $cond = Q::Entity( Q::IsExist(Q::Attrib('value')) ); 

Properties


We'll return to the attributes when it comes to sorting, and now about the conditions on the properties of the entity. A property is a combination of a connection and an entity with which a connection is made. The condition in the request can be simply put on the absence / presence of a property or on the number of properties of the entity. But the most interesting thing is that the property is the objects (I repeat) and therefore the condition can be put on the attributes and properties of the link itself or of the entity with which the link is made. Just think about it, because the connection and the object with which the connection is made can have the same conditions on their attributes and most importantly on their properties, as with the sought-for entity, which allows you to create comprehensive conditions. For example, you can search for entities that have a certain property, while the object that is a property must have a connection (property) with the object, which in turn must have a value attribute containing a certain piece of text. With all this, the condition even can not specify what kind of property - its system name, in particular. The search will operate only on what is known from the condition. In general, abstraction, versatility and flexibility.

So, let's start with the condition for checking the presence of a property in the entities being sought. In fact, the use of the condition function Q :: IsExist is not necessary, since the very fact of specifying a property in a condition determines the presence of a property.

 $cond = Q::Entity( Q::Property( ) ); 

Same:

 $cond = Q::Entity( Q::IsExist(Q::Property()) ); 

The above condition determines the presence of a property, but which one is not specified, therefore the result of a query with this condition will be all entities that have at least one property, that is, the condition is: "Entities that have a property."

The opposite condition is to check the absence of the property. To determine the absence of a property, the function-condition Q :: NotExist is used. The result of the condition below will be all entities with no properties.

 $cond = Q::Entity( Q::NotExist(Q::Property()) ); 

How to set a condition for the presence of a specific property? Q :: Property ($ link_cond, $ entity_cond) has two arguments to determine respectively the conditions for the link and the conditions for the entity with which the link is made. Again, since the connection is also an object, the conditions for it are possible exactly the same as for the entity. For example, an attribute condition.

The following condition uses a condition on the attribute of the property link. A query with this condition will return entities that have a multiple property (size = 0 means multiplicity), and only news has it. To be more precise, the class itself has a link with the size attribute equal to zero (“news”) as the defining comment, and the first news that has two comments, the second news has no comments (see the diagram in the previous article).

 $cond = Q::Entity( Q::Property( Q::Comp(Q::Attrib('size'),'=',0)) ); 

Now we add a condition to the entity with which the link is made — we set a condition on the property of the entity with which the link is made. The following condition will return entities that have a multiple property, in turn, an entity that has a property has no property whatsoever, but associated with an entity whose value attribute contains the “First” text fragment. The condition turned out to be abstract. The result will actually be the first news, since the news item has a comment, the title of which begins with the word "First." The relationship between the news item and the commentary just fits the given condition.

 $cond = Q::Entity( Q::Property( Q::Comp(Q::Attrib('size'),'=',0), //  Q::Entity( //   ,     Q::Property( null, //     Q::Entity(Q::Comp(Q::Attrib('value'),'like','%%')) ) ) ) ); 

Let us demonstrate an example of a condition with counting the number of properties. The following condition will return entities with more than 4 properties. They will be the first news and class category. The news has a link to the category, title, text and two comments - just 5 properties. The class of the category has a name, a description and four links defining the properties for the objects - a total of 6. In the condition we did not specify which links to take into account.

 $cond = Q::Entity( Q::Comp(Q::Count(Q::Property()), '>', 4) ); 

Nothing limits us to add property conditions; you can even perform property counting on entities that play the role of a property on the desired entity.

Entity that is a property


In practice, it is often necessary to search for entities that are properties of other entities. For example, search for comments belonging to a specific news. For these purposes, the conditional argument Q :: IsProperty is similar to the argument-condition property, the only difference is that it defines the relationship and the entity-owner of the relationship. Otherwise, all the same - the conditions on the connection, the conditions on the entity. You can even calculate how many entities refer to the desired one and set a condition that there should be at least 2 owners.

 $cond = Q::Entity( Q::Comp(Q::Count(Q::IsProperty()), '>=', 2) ); 

The given condition will return the classes “string”, “long_string”, “category” and the root category object with the name “Events” - all these entities have more than one owner.

Class membership


Another important and frequently used condition is belonging to the class Q :: IsClass. Belonging to a class is in fact a property of an object, but it requires a special way of checking. It is necessary to take into account the hierarchy of inheritance, that is, the verification of belonging to a class does not boil down to the banal verification of the entity with which the connection was made. For example, the news item belongs to the “news” class, but also belongs to the “content” class and the “id” base class. The following condition will return all comments.

 $cond = Q::Entity( Q::IsClass( Q::Comp(Q::Attrib('sys_name'), '=', 'comment') ) ); 

A class is also an object, so you can specify the condition of the class, as well as entities - check attributes and properties.

The use of the logical functions Q :: LogAnd and Q :: LogOr is demonstrated only in the comparison of attribute values, and in fact they can include arguments-conditions of the properties Q :: Property, Q :: IsProperty, belonging to the class Q :: IsClass and functions Q :: IsExist and Q :: NotExist providing the necessary flexibility.

Sorting


Search without sorting is no good. Using the sort you can find current topics, popular products and much more and use to create the claimed content of the main pages of the site. But how to sort the sought-for entities, taking into account all possible variants of the conditions, when the condition may not even clarify anything about the structure of the entities? In fact, the definition of sorting is quite simple, and it itself is performed directly in the DBMS when executing a search SQL query.

Naturally, you can sort by scalar values ​​only - by attribute values. Sorting parameters are specified in the Q_Attrib attribute condition argument. Thus, the definition of the attribute being sorted is combined with the sorting parameters (descending or ascending, and the sorting order — if there are more than one sorting attribute, then the sorting sequence can be specified) and the condition of the attribute's existence for the sought entities.

You can sort by any attribute that does not necessarily belong to the entity you are looking for, you can even by the attribute of the entity, which is only indirectly connected by a sequence of interrelations with the desired one. Remember, a condition on an attribute can be put on links and objects which are properties? So you can even sort by their attributes. It turns out that the news on the site can be simultaneously sorted by the date of creation and the rating of the author of the news. Or is it the same as sorting purchased products by the name of the stores in which they were bought - by property that has no direct relation to the product.

It is theoretically possible to sort by the number of properties of entities, for example, by the number of news comments, but this feature is temporarily not implemented.

It should also be noted that the attribute (its name) is specified in the sorting parameters, but there is no need to specify the type of the attribute value. The value attribute, in particular, is available for objects of different classes and, most importantly, there are differences in the types of its values ​​for different classes. Lines of different lengths, integers and real numbers. But even under these circumstances, sorting will work. The following condition will return a sorted list of objects that have a value attribute. Sorting will occur by the value attribute in ascending order. The second argument (true) will determine the sorting by this attribute, the third (1) is the sorting order (relevant for several sorted attributes) and the last attribute is the sorting type (false - ascending, true - descending)

 $cond = Q::Entity( Q::IsExist(Q::Attrib('value', null, true, 1, false)) ); //       $q = new Query($cond,0,0); $list = $q->Execute(); 


Conclusion


The proposed search condition creation approach may seem complicated, but only because we are all accustomed to the string description of conditions, as is done in SQL and programming languages. The object approach is convenient, firstly, for software analysis and automatic generation of SQL queries, and secondly, it will be easy for it to make a meaningful GUI interface.

More concern should be the performance question, and what SQL queries are obtained from these mega flexible conditions? Requests are not too scary, but using several LEFT JOINs is a new one and some other designs, without which it is difficult to achieve flexibility.

Project website: boolive.ru

UPD . A year later, I propose to get acquainted with the results of work on the data model in the article Object DB in a relational DBMS .

The development of the project went the other way, realizing the importance of simplicity :)

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


All Articles