CMS architecture. Data model. Part 3

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

    In the store, for example, we do not select the product by its serial number or barcode, without knowing what it means - we look at the properties of the product that interest us. On the main page of the site, again, for example, you need to display the latest news, which boils down to selecting from the database (object data model) 10 objects of the News class with sorting by the date of their creation. To implement such queries, a flexible way is needed to describe the conditions for selecting entities - search conditions, taking into account the features of the object model. Based on the condition, it is necessary to create SQL code for the direct selection from the database of identifiers of entities that satisfy the condition, we have identifiers - we have entities.

    An object approach is used to create a search term. A hierarchy is created from objects representing logical and other functions; theoretically, it can be interpreted as a mathematical function. Below is an example of a condition for retrieving entities of the "news" class. It is decrypted as follows: select entities (Q :: Entity) belonging to the 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 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. The search for entities can be limited by the number and perform an offset as is done by the LIMIT parameter in SQL; the restriction is carried out by the second and third argument 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'); // заголовок первой новости  
    }
    

    The condition is created from the objects of the classes CondAttrib, CondClass, CondComp, CondCount, CondEntity, CondIsExist, CondLink, CondLog, CondNotExist and CondParam, but in order to simplify the syntax instead of directly using the new operator and Cond * classes, the static Q class is used. Using 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, which also play the role of brackets, and a function for comparing attribute values or the result of the counting function using the operations' = ','> ',' <',' <= ','> = ',' <> ','

    Unlike SQL, when fetching, you don’t need to specify where to choose (from which tables), nor do you need joins and grouping of results. In the query, with the help of the condition it is simply indicated WHAT to choose. Since in the object model absolutely everything is an object, there is nothing to choose except for objects, the only thing instead of searching for objects is to find out the number of objects satisfying the condition. Therefore, a condition always begins with an entity argument or entity count function.

    //Условие - все сущности  
    $cond1 = Q::Entity( );  
    //Условие - количество сущностей  
    $cond2 = Q::Count(Q::Entity( )); 
    

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

    We are rarely interested in absolutely all entities, therefore the argument argument condition of the entity Q :: Entity ($ cond) is supplemented by the condition $ cond, it can also be called a filter. It determines what attributes and properties should or should not be in the desired entities, as if continuing the condition: "Entities that have ..."

    Attributes


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

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

    The condition can be supplemented with the logical functions “AND” and “OR” (Q :: LogAnd and Q :: LogOr) if the check on the value or the general condition on the entity is ambiguous. The logical functions “AND” and “OR” also play the role of brackets with which you can create complex conditions of any nesting.

    A query with the following condition will return entities for which the 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 classes and links have the sys_name attribute, therefore the result of the above condition will be the link class and links with the system names label that do not define properties. The result will be heterogeneous entities. Although the given condition is unlikely to have practical application, but it well demonstrates indifference to the types of entities in the search, note that the condition does not clarify the existence of attributes whose values ​​are compared. The search is performed in much the same way as a person does. For example, to find everything that is red among different types of objects: a lamp, sunset, tractor, ball, blood, milk, the CCCP flag :) - the search is carried out without problems, the main thing is to be red.

    Parameter instead of value


    Instead of the scalar value with which the attribute is compared, the Q :: Param parameter can be used in the Q :: Comp comparison function, which allows us to set the parameter value even after the generated request. Thus, one query can be used 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 attribute values, you can simply put a condition on their presence by the function function Q :: IsExist (). The following condition 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 will return to the attributes when it comes to sorting, and now about the conditions for the properties of the entity. A property is a combination of the connection and the entity with which the connection is made. A condition in a query can simply be put on the absence / presence of a property or on the number of properties of an entity. But the most interesting thing is that the property is objects (I repeat) and therefore the condition can be put on the attributes and properties of the connection itself or the entity with which the connection 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 the most important on their properties, as the desired entity, which allows you to create all-encompassing conditions. For example, you can search for entities that have a certain property, while an object being a property must have a connection (property) with the object, which in turn should have a value attribute containing a piece of text. With all this in the condition, you can not even specify what kind of property is its system name, in particular. The search will only operate on what is known from the condition. In general, abstractness, versatility and flexibility.

    So, let's start with the condition of checking for the presence of the property of the required entities. In fact, you do not need to use the condition function Q :: IsExist, since the fact of specifying the property in the condition determines the existence of the property.

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

    Same:

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

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

    The converse is checking for the absence of a property. To determine the absence of a property, the Q :: NotExist condition function is used. The result of the condition below will be all entities that do not have properties.

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

    How to set a condition for a specific property? Q :: Property ($ link_cond, $ entity_cond) has two arguments to determine the conditions for the connection and the conditions for the entity with which the connection is made, respectively. Again, since communication 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 an attribute condition for a property relationship. A query with this condition will return entities that have a multiple property (size = 0 means plurality), and only news has it. To be more precise, the very connection with the size attribute equal to zero is in the news class, which defines comments, and in the first news, which 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 on the entity with which the relationship is made - we put a condition on the property of the entity with which the relationship is made. The following condition will return entities that have a multiple property, in turn, an entity that is a property has its own property without specifying which, but is associated with an entity whose value attribute contains a fragment of the text “First”. The condition turned out to be abstract. The result of it will actually be the first news, since the news object has a comment, the title of which begins with the word “First”. The relationship between the news item and the commentary just fits the 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 that have more than 4 properties. They will be the first news and class category. The news has a link to a category, headline, text and two comments - a total of 5 properties. The category class has a name, description, and four relationships that define properties for objects — a total of 6. In the condition, we did not specify which relationships to consider.

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

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

    Essence Being a Property


    In practice, it is often necessary to search for entities that are properties of other entities. For example, search for comments that belong to a particular news item. For these purposes, the condition argument Q :: IsProperty is used, similar to the property argument argument, the only difference is that it defines the relationship and the entity that owns the relationship. Otherwise, everything is the same - conditions for communication, conditions for the essence. You can even calculate how many entities refer to the desired one and set the condition that there must 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 object of the root category with the name “Events” - all these entities have more than one owner.

    Class membership


    Another important and often used condition is that you belong to the Q :: IsClass class. Belonging to a class is actually 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, verification of class membership does not boil down to a banal verification of the entity with which the connection is made. For example, the news object belongs to the class “news”, but also belongs to the class “content” and the base class “id”. The below 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 refine the class condition, as well as entities - check attributes and properties.

    The use of the Q :: LogAnd and Q :: LogOr logical functions was demonstrated only by comparing attribute values, and they can include conditional arguments to the properties Q :: Property, Q :: IsProperty, belonging to the class Q :: IsClass, and the Q function :: IsExist and Q :: NotExist providing the necessary flexibility.

    Sorting


    A search without sorting is no good. Using sorting, you can find relevant topics, popular products, and much more, and apply them in order to create popular content on the main pages of the site. But how to sort the required entities, taking into account all possible variants of conditions, when the condition may not even specify anything about the structure of entities? In fact, the definition of sorting is quite simple, and it is performed directly in the DBMS when executing a search SQL query.

    Naturally, you can sort by scalar values ​​- by attribute values. Sorting options are specified in the condition argument argument of the Q :: Attrib attribute. Thus, the definition of the sorted attribute and the sorting parameters are combined (descending or ascending, and the sort order - if there are more than one sortable attribute, then you can specify the sort sequence), and the condition for the attribute to exist for the required entities.

    You can sort by any attribute that does not necessarily belong to the desired entities, you can at least by the attribute of the entity, which is only indirectly associated with a series of relationships with the desired one. Remember, a condition on an attribute can be set for relations and objects that 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 rating of the author of the news. Or is it like sorting the purchased products by the name of the stores in which they were purchased - by a property that is not directly related to the product.

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

    It should also be noted that the attribute (its name) is specified in the sorting parameters, but at the same time, clarifications about the type of the attribute value are not required. The value attribute, in particular, exists in objects of different classes and, most importantly, there are differences in the types of its values ​​in different classes. Strings 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 with the value attribute. Sorting will occur according to the value attribute in ascending order. The second argument (true) determines the sorting by this attribute, the third (1) - sort order (relevant for several sortable attributes) and the last attribute sort 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 approach to creating a search term may seem complicated, but only because we are all used to a string description of the conditions, as is done in SQL and programming languages. The object approach is convenient, firstly, for program analysis and automatic generation of SQL queries, and secondly, it will be easy for him to make a meaningful GUI interface.

    Of even greater concern is the question of performance and which SQL queries come from these mega-flexible conditions. Requests are not too scary, but the use of several LEFT JOIN-nov sin and some other designs, without which it is difficult to achieve flexibility.

    Project site: boolive.ru

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

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

    Also popular now: