Bitrix Haterの告白

最近 Bitrixのマイナス点とその否定についての多くの記事で離婚しました。 そのような酒がなくなったので、5セントを追加します。

記事へのコメントの中で、彼らは具体性、例、およびより深いレビューの欠如があると書きました。



この記事は、このレビューを書く試みです。 そうではありませんが、それはむしろ憎しみと苦痛の投稿です(たぶん少し泣き言でも)。 これは、 ピストルの不利な点に関するこのような拡張版です。 私とBitrixの同僚を困らせるほとんどのことを説明しようとします。 毎日多くの苦痛を引き起こすすべての不利な点を1つの投稿にまとめようとします。 最後に、結論を出そうとします。



私は誰ですか? はい、一般に、普通の開発者です。 私は2010年11月(5.5年)からBitrixに取り組んでいます。 私はBitrix のみを使用し、他のCMSで単一の商業プロジェクトを行ったことはありません。サイトの作成にフレームワークを使用していません。 職業別では、主にオンラインストア、その作成、サポート、開発を扱っています。



TL; DR



Bitrix-UG、特別な必要なしにこのプールに入れないでください。



参加する代わりに



まず、思考実験を行うことをお勧めします。 ほぼ同じ年齢でほぼ同じ仕事経験(たとえば、1〜1.5年)の2人のバックエンド開発者を連れて行ってみましょう。1人は1C-Bitrixで、もう1人はSymfony(たとえば)で動作します。 この間ずっと働いていたテクノロジーのセットと、他のどのテクノロジーのセットと、そしてこの間に最終的にどのような知識を得たかを比較するのは簡単です。



Symfony開発者の場合、これは次のようになります:php5 / 7 + OOPの深い理解、一般に受け入れられている設計パターン(少なくともMVC、DI、ファクトリ、リポジトリ)、単体テストの開発、テンプレートエンジンの使用(最小小枝)、ORM(Doctrineを使用)、作曲家、git、PSR標準、コンソールエクスペリエンスとコンソールアプリケーションの作成、基本的なWebサーバー設定スキル。



1C-Bitrixの場合、開発者はphp5、html / css + javascript / jqueryになります(テンプレートエンジンはすぐに使用でき、Bitrixはロジックをテンプレートに固執します、とにかく、あなたはそれをいじる必要があります)、そしておそらくgit(それは会社に大きく依存します、いくつかの恐竜はまだFTP経由で本番環境で見ています)、運が良ければ-少しのSQLと...すべてですか?



これはすべて非常に個人的なものであり、個人の環境が非常に大きな役割を果たす可能性があることを理解しています。 しかし、ここで私はシステム開発者が箱から出して行くことについて話している。 ほとんどの場合、Bitrix開発者は他のフレームワーク/ CMSの開発者に比べてスキルが非常に劣っています-これは紛れもない事実です。 それは、明確なアーキテクチャ、ドキュメント、適切なソリューションが存在しない場合、Bitrixの初期の自由度が高すぎるため、Symfonyは必要なすべてを提供するためです。



1C-Bitrixの世界の外から(地域の)経験豊富な人が私たちの会社にやって来たのは一度だけで、彼は同じ経験を持つ同僚よりも頭と肩が強かった。

私自身もそうです。 残念なことに、最初から、私は同じマーケティングのナンセンスに振り回され、あまり良くない環境に入りました。 私自身は、同じSymfonyで同じ仕事の経験を持っている同僚がより大きな見通しを持っていることを理解し、感じています。これはbitrixの非常に強い副作用です。

これはすべて、Web開発の世界で開発したい場合、間違いなくBitrixを基盤として選択すべきではないことを示唆しています。



2人の開発者を比較して、システムが駆動するフレームワークと、それが提供する自由に注意を喚起したいと思います。 Bitrix、Symfony-どちらもほぼ無限の柔軟性を提供し、原則として、それぞれの組み合わせで、絶対に複雑な製品を作成できます。 ただし、システムは開発者がスティックをホイールに挿入するのではなく、問題を解決するのに役立つはずです。 そして、ここでBitrixは多くを失います。



マーケティング



このことについて少し言いたいだけです これは、Bitrixの成功の主要なコンポーネントです。

開発者向けのドキュメントであっても、Bitrix全体がマーケティング精神に満ちていると言えます。 そこにさえ、彼らの製品は「とてもクールで、すべてのパートナーがそれを評価し、尊重している」と書いています( 証明 、ブロック「構造」)。 Bitrixは、自社製品の提示方法を熟知している優秀なマーケティング担当者を採用しています。 6か月に1回、パートナー向けの会議を開催し、そこで行われたことと計画について話します。 実践が示すように、これらの計画は予定どおりに実現することはなく、リリースが不完全であるか、多くのエラーが発生することが非常に多いです。

例として、販売モジュールのセンセーショナルなリファクタリングは、リリースが1年以上延期され、最新のリリース日( 2015年12月23日 )でさえ3か月間失敗し、新しいストアとBUS(Bitrix ed。 "Site Management")はバージョン16のみがリリースされました2016年3月末。 しかし、結果として、更新後、ユーザーは新しい機能を受け取らなかっただけではありません。 ほとんどの場合、ユーザーは操作不能なストアを受け取り、さらに文書化されていない新しいコードの山を受け取りました。

新しい楽器には、誰もが聞いているような有名な名前が付けられています:複合サイト-加速x100; 高負荷ブロック Bitrix BigData 実際、これらの単語は、名前と一致しない非常に普通のことを隠しています。

残念ながら、このアプローチはどこでも追跡できます。 外では、製品はお菓子のように見えます。 しかし、Bitrixを使用して標準の配信から一歩離れると、それだけです。更新中に機能を維持することは非常に困難です。

ただし、まず最初に、この記事ではマーケティングのトピックが引き続き(おそらく複数回)表示されます。



建築



10年間、Bitrixは必死で行き止まりになりました。 製品の各新機能は、技術的な観点からの詳細な説明なしで、ビジネスの利益に応じて登場しました。 そして、もちろん、これはすべて雪玉のように成長しました。

考えてみると、Bitrixにはそのようなアーキテクチャはありません。 このアーキテクチャに従うことを可能にする、一般に受け入れられた定式化されたルールさえありません。 開発者の過程で、 製品アーキテクチャセクションで、BitrixはMVCアーキテクチャに従い、図を提供すると言われています。



Bitrix MVC



このMVCはクラシックバージョンとは非常に異なることをすぐに言いたいと思います。 概念の非常に強力な代替があり、実際にはMVCはありません。モジュール、コンポーネント、およびコンポーネントテンプレートへの抽象的な分割がいくつかあります。 そして、すでにこれらのレンガからサイト全体が構築されています。 しかし、これらのブリックはそれぞれ異なるタスクを引き受けることができるため、密接に相互接続されています。

アーキテクチャのこれらの各側面をより詳細に検討してみます。



M-モデル、またはAPI



システムAPIをモデルとして判断するのは難しいです。 はい、APIはデータアクセスインターフェイスを提供し、それを操作することができます。 しかし、Bitrix APIを使用すると、データだけでなく、テンプレートやユーザークエリも使用できます。 まあ...それは私の意見です。

現在、Bitrixには2つのAPIオプションがあります。 それらを古いもの新しいものに分けることは条件付きで可能です。 新しいAPIはD7と呼ばれます(正直なところ、なぜか覚えていませんが、 Rizhikovはパートナー会議の1つでこれについて話しました)。



古いAPIはアンチパターンのコレクションであり、悪いコードの恐ろしい例です。 Bitrixでは、非静的メソッドを静的に呼び出すことは常に正常であると考えられていました。 たとえば、よく知られているCIBlockElement :: GetListは、おそらく開発で最も頻繁に使用されるメソッドの1つです。 その実装には、500行を超えるコードが含まれ、グローバルを使用し、恐ろしい巨大なリクエストを作成し、非現実的で単純に読めない、文書化されていないコードが含まれています。

見て
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 :)



All Articles