πŸ“œ ⬆️ ⬇️

Confessions of Bitrix Hayter

Something a lot of recently divorced articles about the bitrix, and their denials . Since I’m so drunk, I’ll add my 5 kopecks too.
In the comments to the articles they wrote that there was a lack of specifics, examples, and a deeper review.

This article is an attempt to write this review. Although no, it is rather a post of hate and pain (maybe even a little whining). This is such an extended version of the post about cons of pistol . I will try to describe most of the things that irritate me and my colleagues in Bitrix. I will try to collect in one post all those disadvantages that cause a lot of pain every day. In the end I will try to draw conclusions.

Who am i? Yes, in general, an ordinary developer. I have been working with Bitrix since November 2010 (5.5 years). I work only with Bitrix, I have not made a single commercial project on other CMS, I have not used frameworks in the creation of sites. By occupation, I mainly deal with online stores, their creation, support and development.
')

TL; DR


Bitrix - UG, you should not climb into this pool without special need.

Instead of intro


For a start, I suggest you conduct a thought experiment. Let's try to take two backend-developers of about the same age and about the same length of service (say, 1 - 1.5 years), only so that one of them worked with this 1C-Bitrix, and the other with Symfony (for example). One can easily compare with which set of technologies one has worked all this time, and with which one another, and as a result, they gained knowledge during this time.

In the case of a symfony developer, it will be: php5 / 7 + deep understanding of OOP, common design patterns (MVC, DI, Factory, Repository at least), ability to develop Unit tests, use templating engines (minimum twig), ORM (with Doctrine), composer , git, PSR standards, console experience and writing console applications, basic web server setup skills.

In the case of 1C-Bitrix developer, this will be php5, html / css + javascript / jquery (there is no template from the template box, and the bitrix puts logic into the templates, anyway, you’ll have to mess around with it), maybe git (and it depends a lot , some dinosaurs are still sawing on production via FTP), if you're lucky - a little sql and ... everything?

I understand that this is all very individual and the human environment can play a very big role. But here I am talking about what the developer of the system is moving out of the box. In most cases, the Bitrix developer is very much inferior in skills compared to developers under other frameworks / CMS - and this is an indisputable fact. That's because Bitrix initially gives you too much freedom in the absence of a clear architecture, documentation, and the right solutions, while Symfony offers everything you need.

Only once did an experienced person not from the world of 1C-Bitrix (in the region) come to our company and he was stronger than his colleagues with the same experience simply due to the fact that he had previously been brainwashed on the right rails.
I myself am so. I, unfortunately, from the very beginning have put dust in the eyes of the same marketing nonsense, and I got into not very good surroundings. I myself understand and feel that my colleagues with similar work experience, but in the same Symfony, have a greater outlook, and this is a very strong side effect from the bitrix.
This all suggests that if you want to develop in the world of web development, then you should definitely not choose the bitrix as the basis.

Comparing the two developers, I want to draw attention to the framework into which the system drives and to the freedom it provides. With Bitrix, Symfony, they both provide almost unlimited flexibility, and in principle you can create a product of absolutely any complexity on each of them. However, the system should help the developer in solving problems, rather than put a stick in the wheel. And here Beatrix loses very much.

Marketing


Just want to say a few words about it, because This is the main component of the success of Bitrix.
We can say that the whole Bitrix is ​​saturated with the marketing spirit, even the documentation for developers. Even there they write that their product is β€œso cool that all our partners appreciate and respect it” ( proof , β€œStructure” block). In the bitrix work good marketers who know how to properly present their product. Every six months, they organize conferences for partners, where they talk about what has been done and what their plans are. As practice shows, these plans never come true on time and very often releases are either incomplete or with a bunch of errors.
As an example, the sensational refactoring of the sale module, the release of which was postponed for over a year, and even the most recent release date ( December 23, 2015 ) failed for 3 months, and released a new store and BEADS (Bitrix red. "Site Management") version 16 only at the end of March 2016 But as a result, after the upgrade, users not only did not receive new features. Users received in most cases an inoperative store, and a hill of new undocumented code to boot.
New tools are given such loud names that everyone knows: Composite site - x100 acceleration; Highload blocks; Bitrix BigData. In fact, these words are hiding quite ordinary things that do not correspond to their name.
And this approach can be traced everywhere, unfortunately. From the outside, the product looks like a candy that you bought, put and use. But if you take a step away from the standard delivery with the bitrix, that's all, maintaining the functionality during upgrades turns into hell.
However, about everything in order, the topic of marketing will still emerge in this post, most likely, more than once.

Architecture


For a dozen years, Bitrix desperately drove himself into a dead end. Each new feature in the product came out in accordance with the interests of the business, without due consideration from a technical point of view. And, of course, it all grew like a snowball.
If you think about it, then there is no architecture in Bitrix as such. There is not even the generally accepted formulated rules that would allow to follow this architecture. In the course of the developers, in the Product Architecture section, it is stated that the bitrix follows the MVC architecture and provides the scheme:

Bitrix MVC

Just want to say that this MVC is very different from the classic version. There is a very strong substitution of concepts, there is actually no MVC here, there is simply some abstract division into modules, components, and component templates. And already from these bricks the whole site is built. But each of these bricks can take on different tasks, and therefore they are closely interrelated.
Let me try to consider each of these aspects of the architecture in more detail.

M - Model, or API


I find it difficult to judge the system API as a model. Yes, the API provides a data access interface and allows you to manipulate them. But the Bitrix API allows you to work not only with data, but also with templates, and with user queries too. Well, okay ... this is just my opinion.
At the moment there are 2 API options in Bitrix. It is conditionally possible to divide them into old and new . The new API is called D7 (honestly - I do not remember why, but Rizhikov talked about this at one of the partner conferences).

The old API is a collection of antipatterns, horrible examples of bad code. In Bitrix, it was always considered normal to call non-static methods statically, and vice versa, to require a state when it is inappropriate. For example, the well-known CIBlockElement :: GetList is perhaps one of the most frequently used methods in development. Its implementation contains more than 500 lines of code, uses globals, builds terrifying, huge requests, and contains unreal, just unreadable undocumented code.
We look
function GetList($arOrder=array("SORT"=>"ASC"), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
    {
        /*
        Filter combinations:
        CHECK_PERMISSIONS="N" - check permissions of the current user to the infoblock
            MIN_PERMISSION="R" - when permissions check, then minimal access level
        SHOW_HISTORY="N" - add history items to list
            SHOW_NEW="N" - if not add history items, then add new, but not published elements
        */
        global $DB, $USER;
        $MAX_LOCK = intval(COption::GetOptionString("workflow","MAX_LOCK_TIME","60"));
        $uid = is_object($USER)? intval($USER->GetID()): 0;

        $formatActiveDates = CPageOption::GetOptionString("iblock", "FORMAT_ACTIVE_DATES", "-") != "-";
        $shortFormatActiveDates = CPageOption::GetOptionString("iblock", "FORMAT_ACTIVE_DATES", "SHORT");

        $arIblockElementFields = array(
                "ID"=>"BE.ID",
                "TIMESTAMP_X"=>$DB->DateToCharFunction("BE.TIMESTAMP_X"),
                "TIMESTAMP_X_UNIX"=>'UNIX_TIMESTAMP(BE.TIMESTAMP_X)',
                "MODIFIED_BY"=>"BE.MODIFIED_BY",
                "DATE_CREATE"=>$DB->DateToCharFunction("BE.DATE_CREATE"),
                "DATE_CREATE_UNIX"=>'UNIX_TIMESTAMP(BE.DATE_CREATE)',
                "CREATED_BY"=>"BE.CREATED_BY",
                "IBLOCK_ID"=>"BE.IBLOCK_ID",
                "IBLOCK_SECTION_ID"=>"BE.IBLOCK_SECTION_ID",
                "ACTIVE"=>"BE.ACTIVE",
                "ACTIVE_FROM"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_FROM", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_FROM)>0, ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "SHORT").")"
                        ),
                "ACTIVE_TO"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_TO", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_TO)>0, ".$DB->DateToCharFunction("BE.ACTIVE_TO", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_TO", "SHORT").")"
                        ),
                "DATE_ACTIVE_FROM"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_FROM", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_FROM)>0, ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "SHORT").")"
                        ),
                "DATE_ACTIVE_TO"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_TO", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_TO)>0, ".$DB->DateToCharFunction("BE.ACTIVE_TO", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_TO", "SHORT").")"
                        ),
                "SORT"=>"BE.SORT",
                "NAME"=>"BE.NAME",
                "PREVIEW_PICTURE"=>"BE.PREVIEW_PICTURE",
                "PREVIEW_TEXT"=>"BE.PREVIEW_TEXT",
                "PREVIEW_TEXT_TYPE"=>"BE.PREVIEW_TEXT_TYPE",
                "DETAIL_PICTURE"=>"BE.DETAIL_PICTURE",
                "DETAIL_TEXT"=>"BE.DETAIL_TEXT",
                "DETAIL_TEXT_TYPE"=>"BE.DETAIL_TEXT_TYPE",
                "SEARCHABLE_CONTENT"=>"BE.SEARCHABLE_CONTENT",
                "WF_STATUS_ID"=>"BE.WF_STATUS_ID",
                "WF_PARENT_ELEMENT_ID"=>"BE.WF_PARENT_ELEMENT_ID",
                "WF_LAST_HISTORY_ID"=>"BE.WF_LAST_HISTORY_ID",
                "WF_NEW"=>"BE.WF_NEW",
                "LOCK_STATUS"=>"if (BE.WF_DATE_LOCK is null, 'green', if(DATE_ADD(BE.WF_DATE_LOCK, interval ".$MAX_LOCK." MINUTE)<now(), 'green', if(BE.WF_LOCKED_BY=".$uid.", 'yellow', 'red')))",
                "WF_LOCKED_BY"=>"BE.WF_LOCKED_BY",
                "WF_DATE_LOCK"=>$DB->DateToCharFunction("BE.WF_DATE_LOCK"),
                "WF_COMMENTS"=>"BE.WF_COMMENTS",
                "IN_SECTIONS"=>"BE.IN_SECTIONS",
                "SHOW_COUNTER"=>"BE.SHOW_COUNTER",
                "SHOW_COUNTER_START"=>$DB->DateToCharFunction("BE.SHOW_COUNTER_START"),
                "CODE"=>"BE.CODE",
                "TAGS"=>"BE.TAGS",
                "XML_ID"=>"BE.XML_ID",
                "EXTERNAL_ID"=>"BE.XML_ID",
                "TMP_ID"=>"BE.TMP_ID",
                "USER_NAME"=>"concat('(',U.LOGIN,') ',ifnull(U.NAME,''),' ',ifnull(U.LAST_NAME,''))",
                "LOCKED_USER_NAME"=>"concat('(',UL.LOGIN,') ',ifnull(UL.NAME,''),' ',ifnull(UL.LAST_NAME,''))",
                "CREATED_USER_NAME"=>"concat('(',UC.LOGIN,') ',ifnull(UC.NAME,''),' ',ifnull(UC.LAST_NAME,''))",
                "LANG_DIR"=>"L.DIR",
                "LID"=>"B.LID",
                "IBLOCK_TYPE_ID"=>"B.IBLOCK_TYPE_ID",
                "IBLOCK_CODE"=>"B.CODE",
                "IBLOCK_NAME"=>"B.NAME",
                "IBLOCK_EXTERNAL_ID"=>"B.XML_ID",
                "DETAIL_PAGE_URL"=>"B.DETAIL_PAGE_URL",
                "LIST_PAGE_URL"=>"B.LIST_PAGE_URL",
                "CANONICAL_PAGE_URL"=>"B.CANONICAL_PAGE_URL",
                "CREATED_DATE"=>$DB->DateFormatToDB("YYYY.MM.DD", "BE.DATE_CREATE"),
                "BP_PUBLISHED"=>"if(BE.WF_STATUS_ID = 1, 'Y', 'N')",
            );
        unset($shortFormatActiveDates);
        unset($formatActiveDates);

        $bDistinct = false;

        CIBlockElement::PrepareGetList(
                $arIblockElementFields,
                $arJoinProps,
                $bOnlyCount,
                $bDistinct,

                $arSelectFields,
                $sSelect,
                $arAddSelectFields,

                $arFilter,
                $sWhere,
                $sSectionWhere,
                $arAddWhereFields,

                $arGroupBy,
                $sGroupBy,

                $arOrder,
                $arSqlOrder,
                $arAddOrderByFields,

                $arIBlockFilter,
                $arIBlockMultProps,
                $arIBlockConvProps,
                $arIBlockAllProps,
                $arIBlockNumProps,
                $arIBlockLongProps
            );

        $arFilterIBlocks = isset($arFilter["IBLOCK_ID"])? array($arFilter["IBLOCK_ID"]): array();
        //******************FROM PART********************************************
        $sFrom = "";
        foreach($arJoinProps["FPS"] as $iblock_id => $iPropCnt)
        {
            $sFrom .= "\t\t\tINNER JOIN b_iblock_element_prop_s".$iblock_id." FPS".$iPropCnt." ON FPS".$iPropCnt.".IBLOCK_ELEMENT_ID = BE.ID\n";
            $arFilterIBlocks[$iblock_id] = $iblock_id;
        }

        foreach($arJoinProps["FP"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN b_iblock_property FP".$i." ON FP".$i.".IBLOCK_ID = B.ID AND ".
                    (
                        IntVal($propID)>0?
                        " FP".$i.".ID=".IntVal($propID)."\n":
                        " FP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );
            else
                $sFrom .= "\t\t\tLEFT JOIN b_iblock_property FP".$i." ON FP".$i.".IBLOCK_ID = B.ID AND ".
                    (
                        IntVal($propID)>0?
                        " FP".$i.".ID=".IntVal($propID)."\n":
                        " FP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["FPV"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            if($db_prop["MULTIPLE"]=="Y")
                $bDistinct = true;

            if($db_prop["VERSION"]==2)
                $strTable = "b_iblock_element_prop_m".$db_prop["IBLOCK_ID"];
            else
                $strTable = "b_iblock_element_property";

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN ".$strTable." FPV".$i." ON FPV".$i.".IBLOCK_PROPERTY_ID = FP".$db_prop["JOIN"].".ID AND FPV".$i.".IBLOCK_ELEMENT_ID = BE.ID\n";
            else
                $sFrom .= "\t\t\tLEFT JOIN ".$strTable." FPV".$i." ON FPV".$i.".IBLOCK_PROPERTY_ID = FP".$db_prop["JOIN"].".ID AND FPV".$i.".IBLOCK_ELEMENT_ID = BE.ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["FPEN"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            if($db_prop["VERSION"] == 2 && $db_prop["MULTIPLE"] == "N")
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND FPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = FPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND FPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = FPEN".$i.".ID\n";
            }
            else
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = FPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND FPV".$db_prop["JOIN"].".VALUE_ENUM = FPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = FPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND FPV".$db_prop["JOIN"].".VALUE_ENUM = FPEN".$i.".ID\n";
            }

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            $sFrom .= "\t\t\tLEFT JOIN b_iblock_element BE".$i." ON BE".$i.".ID = ".
                (
                    $db_prop["VERSION"]==2 && $db_prop["MULTIPLE"]=="N"?
                    "FPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]
                    :"FPV".$db_prop["JOIN"].".VALUE_NUM"
                ).
                (
                    $arFilter["SHOW_HISTORY"] != "Y"?
                    " AND ((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)".($arFilter["SHOW_NEW"]=="Y"? " OR BE.WF_NEW='Y'": "").")":
                    ""
                )."\n";

            if($db_prop["bJoinIBlock"])
                $sFrom .= "\t\t\tLEFT JOIN b_iblock B".$i." ON B".$i.".ID = BE".$i.".IBLOCK_ID\n";

            if($db_prop["bJoinSection"])
                $sFrom .= "\t\t\tLEFT JOIN b_iblock_section BS".$i." ON BS".$i.".ID = BE".$i.".IBLOCK_SECTION_ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FPS"] as $iblock_id => $db_prop)
        {
            $sFrom .= "\t\t\tLEFT JOIN b_iblock_element_prop_s".$iblock_id." JFPS".$db_prop["CNT"]." ON JFPS".$db_prop["CNT"].".IBLOCK_ELEMENT_ID = BE".$db_prop["JOIN"].".ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FP"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];
            list($propID, $link) = explode("~", $propID, 2);

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN b_iblock_property JFP".$i." ON JFP".$i.".IBLOCK_ID = BE".$db_prop["JOIN"].".IBLOCK_ID AND ".
                    (
                        IntVal($propID)>0?
                        " JFP".$i.".ID=".IntVal($propID)."\n":
                        " JFP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );
            else
                $sFrom .= "\t\t\tLEFT JOIN b_iblock_property JFP".$i." ON JFP".$i.".IBLOCK_ID = BE".$db_prop["JOIN"].".IBLOCK_ID AND ".
                    (
                        IntVal($propID)>0?
                        " JFP".$i.".ID=".IntVal($propID)."\n":
                        " JFP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FPV"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];
            list($propID, $link) = explode("~", $propID, 2);

            if($db_prop["MULTIPLE"]=="Y")
                $bDistinct = true;

            if($db_prop["VERSION"]==2)
                $strTable = "b_iblock_element_prop_m".$db_prop["IBLOCK_ID"];
            else
                $strTable = "b_iblock_element_property";

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN ".$strTable." JFPV".$i." ON JFPV".$i.".IBLOCK_PROPERTY_ID = JFP".$db_prop["JOIN"].".ID AND JFPV".$i.".IBLOCK_ELEMENT_ID = BE".$db_prop["BE_JOIN"].".ID\n";
            else
                $sFrom .= "\t\t\tLEFT JOIN ".$strTable." JFPV".$i." ON JFPV".$i.".IBLOCK_PROPERTY_ID = JFP".$db_prop["JOIN"].".ID AND JFPV".$i.".IBLOCK_ELEMENT_ID = BE".$db_prop["BE_JOIN"].".ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FPEN"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];
            list($propID, $link) = explode("~", $propID, 2);

            if($db_prop["VERSION"] == 2 && $db_prop["MULTIPLE"] == "N")
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND JFPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = JFPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND JFPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = JFPEN".$i.".ID\n";
            }
            else
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = JFPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND JFPV".$db_prop["JOIN"].".VALUE_ENUM = JFPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = JFPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND JFPV".$db_prop["JOIN"].".VALUE_ENUM = JFPEN".$i.".ID\n";
            }

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        if(strlen($arJoinProps["BES"]))
        {
            $sFrom .= "\t\t\t".$arJoinProps["BES"]."\n";
        }

        if(strlen($arJoinProps["FC"]))
        {
            $sFrom .= "\t\t\t".$arJoinProps["FC"]."\n";
            $bDistinct = $bDistinct || (isset($arJoinProps["FC_DISTINCT"]) && $arJoinProps["FC_DISTINCT"] == "Y");
        }

        if($arJoinProps["RV"])
            $sFrom .= "\t\t\tLEFT JOIN b_rating_voting RV ON RV.ENTITY_TYPE_ID = 'IBLOCK_ELEMENT' AND RV.ENTITY_ID = BE.ID\n";
        if($arJoinProps["RVU"])
            $sFrom .= "\t\t\tLEFT JOIN b_rating_vote RVU ON RVU.ENTITY_TYPE_ID = 'IBLOCK_ELEMENT' AND RVU.ENTITY_ID = BE.ID AND RVU.USER_ID = ".$uid."\n";
        if($arJoinProps["RVV"])
            $sFrom .= "\t\t\t".($arJoinProps["RVV"]["bFullJoin"]? "INNER": "LEFT")." JOIN b_rating_vote RVV ON RVV.ENTITY_TYPE_ID = 'IBLOCK_ELEMENT' AND RVV.ENTITY_ID = BE.ID\n";

        //******************END OF FROM PART********************************************

        $bCatalogSort = false;
        if(count($arAddSelectFields)>0 || count($arAddWhereFields)>0 || count($arAddOrderByFields)>0)
        {
            if(CModule::IncludeModule("catalog"))
            {
                $res_catalog = CCatalogProduct::GetQueryBuildArrays($arAddOrderByFields, $arAddWhereFields, $arAddSelectFields);
                if(
                    $sGroupBy==""
                    && !$bOnlyCount
                    && !(is_object($this) && isset($this->strField))
                )
                    $sSelect .= $res_catalog["SELECT"]." ";
                $sFrom .= str_replace("LEFT JOIN", "\n\t\t\tLEFT JOIN", $res_catalog["FROM"])."\n";
                //$sWhere .= $res_catalog["WHERE"]." "; moved to MkFilter
                if(is_array($res_catalog["ORDER"]) && count($res_catalog["ORDER"]))
                {
                    $bCatalogSort = true;
                    foreach($res_catalog["ORDER"] as $i=>$val)
                        $arSqlOrder[$i] = $val;
                }
            }
        }

        $i = array_search("CREATED_BY_FORMATTED", $arSelectFields);
        if ($i !== false)
        {
            if (
                $sSelect
                && $sGroupBy==""
                && !$bOnlyCount
                && !(is_object($this) && isset($this->strField))
            )
            {
                $sSelect .= ",UC.NAME UC_NAME, UC.LAST_NAME UC_LAST_NAME, UC.SECOND_NAME UC_SECOND_NAME, UC.EMAIL UC_EMAIL, UC.ID UC_ID, UC.LOGIN UC_LOGIN";
            }
            else
            {
                unset($arSelectFields[$i]);
            }
        }

        $sOrderBy = "";
        foreach($arSqlOrder as $i=>$val)
        {
            if(strlen($val))
            {
                if($sOrderBy=="")
                    $sOrderBy = " ORDER BY ";
                else
                    $sOrderBy .= ",";

                $sOrderBy .= $val." ";
            }
        }

        $sSelect = trim($sSelect, ", \t\n\r");
        if(strlen($sSelect) <= 0)
            $sSelect = "0 as NOP ";

        $bDistinct = $bDistinct || (isset($arFilter["INCLUDE_SUBSECTIONS"]) && $arFilter["INCLUDE_SUBSECTIONS"] == "Y");

        if($bDistinct)
            $sSelect = str_replace("%%_DISTINCT_%%", "DISTINCT", $sSelect);
        else
            $sSelect = str_replace("%%_DISTINCT_%%", "", $sSelect);

        $sFrom = "
            b_iblock B
            INNER JOIN b_lang L ON B.LID=L.LID
            INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID
            ".ltrim($sFrom, "\t\n")
            .(in_array("USER_NAME", $arSelectFields)? "\t\t\tLEFT JOIN b_user U ON U.ID=BE.MODIFIED_BY\n": "")
            .(in_array("LOCKED_USER_NAME", $arSelectFields)? "\t\t\tLEFT JOIN b_user UL ON UL.ID=BE.WF_LOCKED_BY\n": "")
            .(in_array("CREATED_USER_NAME", $arSelectFields) || in_array("CREATED_BY_FORMATTED", $arSelectFields)? "\t\t\tLEFT JOIN b_user UC ON UC.ID=BE.CREATED_BY\n": "")."
        ";

        $strSql = "
            FROM ".$sFrom."
            WHERE 1=1 "
            .$sWhere."
            ".$sGroupBy."
        ";

        if(isset($this) && is_object($this) && isset($this->strField))
        {
            $this->sFrom = $sFrom;
            $this->sWhere = $sWhere;
            return "SELECT ".$sSelect.$strSql;
        }

        if($bOnlyCount)
        {
            $res = $DB->Query("SELECT ".$sSelect.$strSql, false, "FILE: ".__FILE__."<br> LINE: ".__LINE__);
            $res = $res->Fetch();
            return $res["CNT"];
        }

        if(is_array($arNavStartParams))
        {
            $nTopCount = intval($arNavStartParams["nTopCount"]);
            $nElementID = intval($arNavStartParams["nElementID"]);

            if($nTopCount > 0)
            {
                $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy." LIMIT ".$nTopCount;
                $res = $DB->Query($strSql);
            }
            elseif(
                $nElementID > 0
                && $sGroupBy == ""
                && $sOrderBy != ""
                && strpos($sSelect, "BE.ID") !== false
                && !$bCatalogSort
            )
            {
                $nPageSize = intval($arNavStartParams["nPageSize"]);

                if($nPageSize > 0)
                {
                    $DB->Query("SET @rank_e=0");

                    $DB->Query("SET @rank_r=0");
                    $DB->Query("
                        SELECT
                            ".$sSelect."
                            ,@rank_r:=@rank_r+1 AS rank1
                            ,if (BE.ID = ".$nElementID.", @rank_e:=@rank_r, null) rank2
                        ".$strSql.$sOrderBy."
                    ");

                    $DB->Query("SET @rank_r=0");
                    $res = $DB->Query("
                        SELECT *
                        FROM (
                            SELECT
                                ".$sSelect."
                                ,@rank_r:=@rank_r+1 AS RANK
                            ".$strSql.$sOrderBy."
                            LIMIT 18446744073709551615
                        ) el0
                        WHERE el0.RANK between @rank_e-$nPageSize and @rank_e+$nPageSize
                    ");
                }
                else
                {
                    $DB->Query("SET @rank=0");
                    $res = $DB->Query("
                        SELECT *
                        FROM (
                            SELECT
                                ".$sSelect."
                                ,@rank:=@rank+1 AS RANK
                            ".$strSql.$sOrderBy."
                            LIMIT 18446744073709551615
                        ) el0
                        WHERE el0.ID = ".$nElementID."
                    ");
                }
            }
            else
            {
                if($sGroupBy == "")
                {
                    $res_cnt = $DB->Query("SELECT COUNT(".($bDistinct? "DISTINCT BE.ID": "'x'").") as C ".$strSql);
                    $res_cnt = $res_cnt->Fetch();
                    $cnt = $res_cnt["C"];
                }
                else
                {
                    $res_cnt = $DB->Query("SELECT 'x' ".$strSql);
                    $cnt = $res_cnt->SelectedRowsCount();
                }

                $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy;
                $res = new CDBResult();
                $res->NavQuery($strSql, $cnt, $arNavStartParams);
            }
        }
        else//if(is_array($arNavStartParams))
        {
            $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy;
            $res = $DB->Query($strSql, false, "FILE: ".__FILE__."<br> LINE: ".__LINE__);
        }

        $res = new CIBlockResult($res);
        $res->SetIBlockTag($arFilterIBlocks);
        $res->arIBlockMultProps = $arIBlockMultProps;
        $res->arIBlockConvProps = $arIBlockConvProps;
        $res->arIBlockAllProps  = $arIBlockAllProps;
        $res->arIBlockNumProps = $arIBlockNumProps;
        $res->arIBlockLongProps = $arIBlockLongProps;

        return $res;
    }



, , CIBlockElement. , , , , .

API , $APPLICATION, $USER, $DB. , , .
, , , , $APPLICATION->ThrowException(), .
    public function ThrowException($msg, $id = false)
    {
        $this->ResetException();
        if(is_object($msg) && (is_subclass_of($msg, 'CApplicationException') || (strtolower(get_class($msg))=='capplicationexception')))
            $this->LAST_ERROR = $msg;
        else
            $this->LAST_ERROR = new CApplicationException($msg, $id);
    }


β€” , .. D7 API. , . , API .

API . -, , . , CIBlockElement::GetList β€” Bitrix\Iblock\ElementTable::getList, β€” , β€” . , , \Bitrix\Main\Loader::autoLoad, PSR-0/4.

,
        public static function autoLoad($className)
	{
		$file = ltrim($className, "\\");    // fix web env
		$file = strtr($file, static::ALPHA_UPPER, static::ALPHA_LOWER);

		static $documentRoot = null;
		if ($documentRoot === null)
			$documentRoot = static::getDocumentRoot();

		if (isset(self::$arAutoLoadClasses[$file]))
		{
			$pathInfo = self::$arAutoLoadClasses[$file];
			if ($pathInfo["module"] != "")
			{
				$m = $pathInfo["module"];
				$h = isset(self::$arLoadedModulesHolders[$m]) ? self::$arLoadedModulesHolders[$m] : 'bitrix';
				include_once($documentRoot."/".$h."/modules/".$m."/" .$pathInfo["file"]);
			}
			else
			{
				require_once($documentRoot.$pathInfo["file"]);
			}
			return;
		}

		if (preg_match("#[^\\\\/a-zA-Z0-9_]#", $file))
			return;

		if (substr($file, -5) == "table")
			$file = substr($file, 0, -5);

		$file = str_replace('\\', '/', $file);
		$arFile = explode("/", $file);

		if ($arFile[0] === "bitrix")
		{
			array_shift($arFile);

			if (empty($arFile))
				return;

			$module = array_shift($arFile);
			if ($module == null || empty($arFile))
				return;
		}
		else
		{
			$module1 = array_shift($arFile);
			$module2 = array_shift($arFile);
			if ($module1 == null || $module2 == null || empty($arFile))
				return;

			$module = $module1.".".$module2;
		}

		if (!isset(self::$arLoadedModulesHolders[$module]))
			return;

		$filePath = $documentRoot."/".self::$arLoadedModulesHolders[$module]."/modules/".$module."/lib/".implode("/", $arFile).".php";

		if (file_exists($filePath))
			require_once($filePath);
	}



API Singleton:


, ServiceLayer' ( \Bitrix\Main\ServiceManager , ). .

ORM β€” D7, -, ! ORM . Table (ElementTable, SectionTable, OrderTable ..). , , ORM Table. , ElementTable element.php. lib ( D7) iblock. , ORM, β€” -.

iblock

ORM, , . , . ActiveRecord Repository .

ORM
<?
namespace Bitrix\Iblock;

use Bitrix\Main;
use Bitrix\Main\Localization\Loc;

Loc::loadMessages(__FILE__);

/**
 * Class ElementTable
 *
 * Fields:
 * <ul>
 * <li> ID int mandatory
 * <li> TIMESTAMP_X datetime optional
 * <li> MODIFIED_BY int optional
 * <li> DATE_CREATE datetime optional
 * <li> CREATED_BY int optional
 * <li> IBLOCK_ID int mandatory
 * <li> IBLOCK_SECTION_ID int optional
 * <li> ACTIVE bool optional default 'Y'
 * <li> ACTIVE_FROM datetime optional
 * <li> ACTIVE_TO datetime optional
 * <li> SORT int optional default 500
 * <li> NAME string(255) mandatory
 * <li> PREVIEW_PICTURE int optional
 * <li> PREVIEW_TEXT string optional
 * <li> PREVIEW_TEXT_TYPE enum ('text', 'html') optional default 'text'
 * <li> DETAIL_PICTURE int optional
 * <li> DETAIL_TEXT string optional
 * <li> DETAIL_TEXT_TYPE enum ('text', 'html') optional default 'text'
 * <li> SEARCHABLE_CONTENT string optional
 * <li> WF_STATUS_ID int optional default 1
 * <li> WF_PARENT_ELEMENT_ID int optional
 * <li> WF_NEW enum ('N', 'Y') optional
 * <li> WF_LOCKED_BY int optional
 * <li> WF_DATE_LOCK datetime optional
 * <li> WF_COMMENTS string optional
 * <li> IN_SECTIONS bool optional default 'N'
 * <li> XML_ID string(255) optional
 * <li> CODE string(255) optional
 * <li> TAGS string(255) optional
 * <li> TMP_ID string(40) optional
 * <li> WF_LAST_HISTORY_ID int optional
 * <li> SHOW_COUNTER int optional
 * <li> SHOW_COUNTER_START datetime optional
 * <li> PREVIEW_PICTURE_FILE reference to {@link \Bitrix\File\FileTable}
 * <li> DETAIL_PICTURE_FILE reference to {@link \Bitrix\File\FileTable}
 * <li> IBLOCK reference to {@link \Bitrix\Iblock\IblockTable}
 * <li> WF_PARENT_ELEMENT reference to {@link \Bitrix\Iblock\IblockElementTable}
 * <li> IBLOCK_SECTION reference to {@link \Bitrix\Iblock\IblockSectionTable}
 * <li> MODIFIED_BY_USER reference to {@link \Bitrix\User\UserTable}
 * <li> CREATED_BY_USER reference to {@link \Bitrix\User\UserTable}
 * <li> WF_LOCKED_BY_USER reference to {@link \Bitrix\User\UserTable}
 * </ul>
 *
 * @package Bitrix\Iblock
 **/

class ElementTable extends Main\Entity\DataManager
{
	const TYPE_TEXT = 'text';
	const TYPE_HTML = 'html';

	/**
	 * Returns DB table name for entity.
	 *
	 * @return string
	 */
	public static function getTableName()
	{
		return 'b_iblock_element';
	}

	/**
	 * Returns entity map definition.
	 *
	 * @return array
	 */
	public static function getMap()
	{
		return array(
			'ID' => new Main\Entity\IntegerField('ID', array(
				'primary' => true,
				'autocomplete' => true,
				'title' => Loc::getMessage('ELEMENT_ENTITY_ID_FIELD'),
			)),
			'TIMESTAMP_X' => new Main\Entity\DatetimeField('TIMESTAMP_X', array(
				'default_value' => new Main\Type\DateTime(),
				'title' => Loc::getMessage('ELEMENT_ENTITY_TIMESTAMP_X_FIELD'),
			)),
			'MODIFIED_BY' => new Main\Entity\IntegerField('MODIFIED_BY', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_MODIFIED_BY_FIELD'),
			)),
			'DATE_CREATE' => new Main\Entity\DatetimeField('DATE_CREATE', array(
				'default_value' => new Main\Type\DateTime(),
				'title' => Loc::getMessage('ELEMENT_ENTITY_DATE_CREATE_FIELD'),
			)),
			'CREATED_BY' => new Main\Entity\IntegerField('CREATED_BY', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_CREATED_BY_FIELD'),
			)),
			'IBLOCK_ID' => new Main\Entity\IntegerField('IBLOCK_ID', array(
				'required' => true,
				'title' => Loc::getMessage('ELEMENT_ENTITY_IBLOCK_ID_FIELD'),
			)),
			'IBLOCK_SECTION_ID' => new Main\Entity\IntegerField('IBLOCK_SECTION_ID', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_IBLOCK_SECTION_ID_FIELD'),
			)),
			'ACTIVE' => new Main\Entity\BooleanField('ACTIVE', array(
				'values' => array('N', 'Y'),
				'default_value' => 'Y',
				'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_FIELD'),
			)),
			'ACTIVE_FROM' => new Main\Entity\DatetimeField('ACTIVE_FROM', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_FROM_FIELD'),
			)),
			'ACTIVE_TO' => new Main\Entity\DatetimeField('ACTIVE_TO', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_TO_FIELD'),
			)),
			'SORT' => new Main\Entity\IntegerField('SORT', array(
				'default_value' => 500,
				'title' => Loc::getMessage('ELEMENT_ENTITY_SORT_FIELD'),
			)),
			'NAME' => new Main\Entity\StringField('NAME', array(
				'required' => true,
				'validation' => array(__CLASS__, 'validateName'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_NAME_FIELD'),
			)),
			'PREVIEW_PICTURE' => new Main\Entity\IntegerField('PREVIEW_PICTURE', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_PICTURE_FIELD'),
			)),
			'PREVIEW_TEXT' => new Main\Entity\TextField('PREVIEW_TEXT', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_TEXT_FIELD'),
			)),
			'PREVIEW_TEXT_TYPE' => new Main\Entity\EnumField('PREVIEW_TEXT_TYPE', array(
				'values' => array(self::TYPE_TEXT, self::TYPE_HTML),
				'default_value' => self::TYPE_TEXT,
				'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_TEXT_TYPE_FIELD'),
			)),
			'DETAIL_PICTURE' => new Main\Entity\IntegerField('DETAIL_PICTURE', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_PICTURE_FIELD'),
			)),
			'DETAIL_TEXT' => new Main\Entity\TextField('DETAIL_TEXT', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_TEXT_FIELD'),
			)),
			'DETAIL_TEXT_TYPE' => new Main\Entity\EnumField('DETAIL_TEXT_TYPE', array(
				'values' => array(self::TYPE_TEXT, self::TYPE_HTML),
				'default_value' => self::TYPE_TEXT,
				'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_TEXT_TYPE_FIELD'),
			)),
			'SEARCHABLE_CONTENT' => new Main\Entity\TextField('SEARCHABLE_CONTENT', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_SEARCHABLE_CONTENT_FIELD'),
			)),
			'WF_STATUS_ID' => new Main\Entity\IntegerField('WF_STATUS_ID', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_STATUS_ID_FIELD'),
			)),
			'WF_PARENT_ELEMENT_ID' => new Main\Entity\IntegerField('WF_PARENT_ELEMENT_ID', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_PARENT_ELEMENT_ID_FIELD'),
			)),
			'WF_NEW' => new Main\Entity\EnumField('WF_NEW', array(
				'values' => array('N', 'Y'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_NEW_FIELD'),
			)),
			'WF_LOCKED_BY' => new Main\Entity\IntegerField('WF_LOCKED_BY', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_LOCKED_BY_FIELD'),
			)),
			'WF_DATE_LOCK' => new Main\Entity\DatetimeField('WF_DATE_LOCK', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_DATE_LOCK_FIELD'),
			)),
			'WF_COMMENTS' => new Main\Entity\TextField('WF_COMMENTS', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_COMMENTS_FIELD'),
			)),
			'IN_SECTIONS' => new Main\Entity\BooleanField('IN_SECTIONS', array(
				'values' => array('N', 'Y'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_IN_SECTIONS_FIELD'),
			)),
			'XML_ID' => new Main\Entity\StringField('XML_ID', array(
				'validation' => array(__CLASS__, 'validateXmlId'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_XML_ID_FIELD'),
			)),
			'CODE' => new Main\Entity\StringField('CODE', array(
				'validation' => array(__CLASS__, 'validateCode'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_CODE_FIELD'),
			)),
			'TAGS' => new Main\Entity\StringField('TAGS', array(
				'validation' => array(__CLASS__, 'validateTags'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_TAGS_FIELD'),
			)),
			'TMP_ID' => new Main\Entity\StringField('TMP_ID', array(
				'validation' => array(__CLASS__, 'validateTmpId'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_TMP_ID_FIELD'),
			)),
			'SHOW_COUNTER' => new Main\Entity\IntegerField('SHOW_COUNTER', array(
				'default_value' => 0,
				'title' => Loc::getMessage('ELEMENT_ENTITY_SHOW_COUNTER_FIELD'),
			)),
			'SHOW_COUNTER_START' => new Main\Entity\DatetimeField('SHOW_COUNTER_START', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_SHOW_COUNTER_START_FIELD'),
			)),
			'PREVIEW_PICTURE_FILE' => new Main\Entity\ReferenceField(
				'PREVIEW_PICTURE_FILE',
				'Bitrix\File\File',
				array('=this.PREVIEW_PICTURE' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'DETAIL_PICTURE_FILE' => new Main\Entity\ReferenceField(
				'DETAIL_PICTURE_FILE',
				'Bitrix\File\File',
				array('=this.DETAIL_PICTURE' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'IBLOCK' => new Main\Entity\ReferenceField(
				'IBLOCK',
				'Bitrix\Iblock\Iblock',
				array('=this.IBLOCK_ID' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'WF_PARENT_ELEMENT' => new Main\Entity\ReferenceField(
				'WF_PARENT_ELEMENT',
				'Bitrix\Iblock\Element',
				array('=this.WF_PARENT_ELEMENT_ID' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'IBLOCK_SECTION' => new Main\Entity\ReferenceField(
				'IBLOCK_SECTION',
				'Bitrix\Iblock\Section',
				array('=this.IBLOCK_SECTION_ID' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'MODIFIED_BY_USER' => new Main\Entity\ReferenceField(
				'MODIFIED_BY_USER',
				'Bitrix\User\User',
				array('=this.MODIFIED_BY' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'CREATED_BY_USER' => new Main\Entity\ReferenceField(
				'CREATED_BY_USER',
				'Bitrix\User\User',
				array('=this.CREATED_BY' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'WF_LOCKED_BY_USER' => new Main\Entity\ReferenceField(
				'WF_LOCKED_BY_USER',
				'Bitrix\User\User',
				array('=this.WF_LOCKED_BY' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
		);
	}
	/**
	 * Returns validators for NAME field.
	 *
	 * @return array
	 */
	public static function validateName()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}

	/**
	 * Returns validators for XML_ID field.
	 *
	 * @return array
	 */
	public static function validateXmlId()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}
	/**
	 * Returns validators for CODE field.
	 *
	 * @return array
	 */
	public static function validateCode()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}
	/**
	 * Returns validators for TAGS field.
	 *
	 * @return array
	 */
	public static function validateTags()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}
	/**
	 * Returns validators for TMP_ID field.
	 *
	 * @return array
	 */
	public static function validateTmpId()
	{
		return array(
			new Main\Entity\Validator\Length(null, 40),
		);
	}

	/**
	 * Add iblock element.
	 *
	 * @param array $data			Element data.
	 * @return Main\Entity\AddResult
	 */
	public static function add(array $data)
	{
		$result = new Main\Entity\AddResult();
		$result->addError(new Main\Entity\EntityError(
			Loc::getMessage('ELEMENT_ENTITY_MESS_ADD_BLOCKED')
		));
		return $result;
	}

	/**
	 * Updates iblock element by primary key.
	 *
	 * @param mixed $primary		Element primary key.
	 * @param array $data			Element data.
	 * @return Main\Entity\UpdateResult
	 */
	public static function update($primary, array $data)
	{
		$result = new Main\Entity\UpdateResult();
		$result->addError(new Main\Entity\EntityError(
			Loc::getMessage('ELEMENT_ENTITY_MESS_UPDATE_BLOCKED')
		));
		return $result;
	}

	/**
	 * Deletes iblock element by primary key.
	 *
	 * @param mixed $primary		Element primary key.
	 * @return Main\Entity\DeleteResult
	 */
	public static function delete($primary)
	{
		$result = new Main\Entity\DeleteResult();
		$result->addError(new Main\Entity\EntityError(
			Loc::getMessage('ELEMENT_ENTITY_MESS_DELETE_BLOCKED')
		));
		return $result;
	}
}


// 
$dbElements = Bitrix\Iblock\ElementTable::query()
	->setFilter(['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'])
	->setSelect(['NAME', 'ID', 'DETAIL_PAGE_URL', 'DATE_ACTIVE_FROM'])
	->addSelect('IBLOCK_SECTION_ID', 'PARENT_SECTION')
	->setLimit(10)
	->addOrder('id', 'DESC')
	->exec();

while ($arElement = $dbElements->fetch()) {
	echo "{$arElement['NAME']} - " . $arElement['DATE_ACTIVE_FROM']->format('d.m.Y H:i:s');
}

// 
$addResult = Bitrix\Iblock\ElementTable::add([
	'NAME' => '  ', 
	'IBLOCK_ID' => CATALOG_IBLOCK_ID
]);
if (!$addResult->isSuccess()) {
	echo implode('<br>' ,$addResult->getErrorMessages());
}




Highload-, D7.
. , , , (1 «» 2 ). . 15 , 500 , . . , , 2.0 (, ) β€” .
. 400 . , Highload-! β€” . highload- + . highload , !
, , , D7, eval' . highload.
public static function compileEntity($hlblock)
    {
        global $USER_FIELD_MANAGER;

        // generate entity & data manager
        $fieldsMap = array();

        // add ID
        $fieldsMap['ID'] = array(
            'data_type' => 'integer',
            'primary' => true,
            'autocomplete' => true
        );

        // build datamanager class
        $entity_name = $hlblock['NAME'];
        $entity_data_class = $hlblock['NAME'];

        if (!preg_match('/^[a-z0-9_]+$/i', $entity_data_class))
        {
            throw new Main\SystemException(sprintf(
                'Invalid entity name `%s`.', $entity_data_class
            ));
        }

        $entity_data_class .= 'Table';

        if (class_exists($entity_data_class))
        {
            // rebuild if it's already exists
            Entity\Base::destroy($entity_data_class);
        }
        else
        {
            $entity_table_name = $hlblock['TABLE_NAME'];

            // make with an empty map
            $eval = '
                class '.$entity_data_class.' extends '.__NAMESPACE__.'\DataManager
                {
                    public static function getTableName()
                    {
                        return '.var_export($entity_table_name, true).';
                    }

                    public static function getMap()
                    {
                        return '.var_export($fieldsMap, true).';
                    }

                    public static function getHighloadBlock()
                    {
                        return '.var_export($hlblock, true).';
                    }
                }
            ';

            eval($eval);
        }

        // then configure and attach fields
        /** @var \Bitrix\Main\Entity\DataManager $entity_data_class */
        $entity = $entity_data_class::getEntity();

        $uFields = $USER_FIELD_MANAGER->getUserFields('HLBLOCK_'.$hlblock['ID']);

        foreach ($uFields as $uField)
        {
            if ($uField['MULTIPLE'] == 'N')
            {
                // just add single field
                $field = $USER_FIELD_MANAGER->getEntityField($uField, $uField['FIELD_NAME']);
                $entity->addField($field);

                foreach ($USER_FIELD_MANAGER->getEntityReferences($uField, $field) as $reference)
                {
                    $entity->addField($reference);
                }
            }
            else
            {
                // build utm entity
                static::compileUtmEntity($entity, $uField);
            }
        }

        return Entity\Base::getInstance($entity_name);
    }


, . , - . , , «», HL - , , , BrandReference. , , ( , ), - , .

C β€” Controller,


Yii. , , - , - , . , , , , . , , , «» . … . result_modifier.php, . , ( ). β€” ? php ? .
…

2 2.0 ( ) β€” . β€” . β€” +, URL , . :


, . … , (urlrewrite.php) . , - - - . .
. , , .
β€” . , , sitemap.xml. . (, - , URL).

, . URL , . , .
. . , - - … , .

β€” , . β€” , , .
component.php. 12 ( 16, 4 ) Β« Β» . , component.php class.php, , \CBitrixComponent. , .. result_modifier.php , , . . , 25-30 . , .. .
, , - ,

V β€” View,


:


. , . , twig blade, . . . , , .. .

.
$APPLICATION->IncludeComponent('bitrix:catalog.section', 'template_name', []);

. , :

…

: header.php, footer.php (, ), description.php ( ), template_styles.css ( ), . . , . .

. - «» , php , , . , YML . , , , 2 . , , /bitrix/modules/catalog/load/yandex_run.php



, . .
β€” CMS. - :


. , . β€” , . php , , . , php , . β€” php ? , . .
, , . , . β€” .



, . , β€” Β«- Β». , - , - , , . β€” :


, .


- , , , . . , β€” . , over 4 . IDE . php, js, html. , SQL , , .
β€” . . .
, ,

js-


js , . :


, . 85, . , (, asset-management).


, , - . β€” . β€” . , β€” . . .

Asset-management CDN


Β« Β» . , «». β€” css/js , - . - , . , script link. , 30-50 .
-, , , . , 50 2, 300-500. - , .
β€” CDN . …
Google Pagespeed Insights, . , body, .
, .
, scss, TypeScript. β€” , webpack, .

/


, , , . , . β€” , ( . ).
, , . . . - /, - , .
, , . , , .
. , - , , , (- , ).


. , - , . , , /bitrix/admin, . , . , β€” .


, . . , , COption ( ). 3-4 , . urlrewrite ! .settings .settings_extra. , , , . .
. … β€” ?

1


, . 2 1, .
, , .
-, Β« Β» , , β€” , 1, . β€” , , .
-, 1 .
-, . , , , 1, . . , .
, , , .
, 1. .

1 β€” . \CIBlockCMLImport.- 5.7 . , β€” \CIBlockCMLImport::ImportElement, 1 . , , 1. - . , β€” , - , , .
, .


, . , , - .
UserFields. . , . () () , . ?
sale , . , ?

24


. . 2 24 β€” SaaS Standlone. 24, SaaS ! , 200 , , , 24 24. .
24 . , , 24.

24, , . , , . , , ,

, 24 β€” . , js , ajax php , html+js. , .


1-1.5 . phpDoc', Β« Β», IDE.
«», .
, , , , . Cookbook Symfony, , . , , , .


- . ( ) , , 530
$ du -s *|sort -nr|cut -f 2-|while read a;do du -hs $a;done
523M	bitrix
204K	upload
 64K	bitrixsetup.php
 56K	desktop_app
 20K	readme.html
 20K	license.html
4,0K	web.config
4,0K	urlrewrite.php
4,0K	readme.php
4,0K	license.php
4,0K	install.config
4,0K	index.php

β€” , - . , . , . , , β€” , : , β€” git.
. , , . .
, , , , .
2 . β€” , . β€” , , ) , / www.bitrixsoft.com, , .


, .
, , , . ( ), 1-. , 95% Β«-Β». , , . .


, . - , . , .

. β€” , , , . β€” , , .
β€” , , Symfony, Laravel, Yii. , , , , .

, ? , . - ( ). , .

β€” php , , Symfony, Laravel, Yii, ZendFramework. , . - . , , - , .

β€” , , , , 1-. , composer.

β€” , . , . , . - //, , , .

UPD. , . , . . , , .
image

nook :)

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


All Articles