Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • Windows 7, XP
  • Eliminarea obiectelor marcate, înlocuirea legăturilor. Aplicație obișnuită și gestionată

Eliminarea obiectelor marcate, înlocuirea legăturilor. Aplicație obișnuită și gestionată

Au apărut o mulțime de articole în ultimul timp despre dezavantajele Bitrix și despre respingerea acestora. Deoarece băutul a continuat așa, voi adăuga cei 5 cenți ai mei.
În comentariile la articole au scris că a existat o lipsă de detalii, exemple și o revizuire mai aprofundată.

Acest articol este o încercare de a scrie această recenzie. Deși nu, acesta este mai degrabă un post de ură și durere (poate chiar un pic de scâncet). Aceasta este o versiune extinsă a postării despre dezavantajele pistolului. Voi încerca să descriu majoritatea lucrurilor care mă irită pe mine și pe colegii mei de la Bitrix. Voi încerca să adun într-o singură postare toate acele dezavantaje care provoacă multă durere în fiecare zi. Până la urmă voi încerca să trag concluzii.

Cine sunt? Da, în general, un dezvoltator obișnuit. Lucrez cu Bitrix din noiembrie 2010 (5,5 ani). Lucru numai cu Bitrix, nu a creat un singur proiect comercial folosind alt CMS, nu a folosit cadre pentru a crea site-uri web. După linie de activitate, mă ocup în principal de magazine online, crearea, sprijinirea și dezvoltarea acestora.

TL;DR

Bitrix - UG, nu ar trebui să intri în această piscină decât dacă este absolut necesar.

În loc de o introducere

Pentru început, vă sugerez să efectuați un experiment de gândire. Să încercăm să luăm doi dezvoltatori backend de aproximativ aceeași vârstă și cu aproximativ aceeași experiență de lucru (să zicem 1 - 1,5 ani), doar ca unul dintre ei să fi lucrat în tot acest timp cu 1C-Bitrix, iar celălalt cu Symfony ( de exemplu). Puteți compara cu ușurință cu ce set de tehnologii a lucrat unul în tot acest timp și cu ce a lucrat celălalt și ce set de cunoștințe au primit în cele din urmă în acest timp.

În cazul unui dezvoltator Symfony, acesta va fi: php5/7 + înțelegere profundă a OOP, modele de design general acceptate (MVC, DI, Factory, Repository cel puțin), capacitatea de a dezvolta teste unitare, folosirea motoarelor de șablon (cel puțin twig ), ORM (cu Doctrine), compozitor , git, standarde PSR, experiență de lucru cu consola și scrierea de aplicații pentru consolă, abilități de bază în configurarea unui server web.

În cazul unui dezvoltator 1C-Bitrix, acesta va fi php5, html/css + javascript/jquery (nu există motoare de șabloane din cutie, iar Bitrix pune logica în șabloane, orice ar spune cineva, va trebui să jucați cu ea), poate git (și asta depinde foarte mult de companie, unii dinozauri sunt încă tăiați în producție prin FTP), dacă aveți noroc - puțin sql și... asta-i tot?

Înțeleg că totul este foarte individual și mediul unei persoane poate juca un rol foarte important. Dar aici vorbesc despre ceea ce dezvoltatorul de sistem este determinat să facă din cutie. În cele mai multe cazuri, un dezvoltator Bitrix este mult inferior în abilități în comparație cu dezvoltatorii pentru alte cadre/CMS - și acesta este un fapt incontestabil. Și totul pentru că Bitrix oferă inițial prea multă libertate în absența unei arhitecturi clare, a documentației și a soluțiilor corecte, în timp ce Symfony oferă tot ce ai nevoie.

O singură dată a venit în compania noastră o persoană cu experiență, nu din lumea 1C-Bitrix (din regiune) și s-a aflat cu cap și umeri deasupra colegilor săi cu aceeași experiență, pur și simplu datorită faptului că anterior fusese pus pe drumul cel bun.
Eu însumi sunt așa. Din păcate, încă de la început am fost păcălit de aceleași prostii de marketing și am ajuns într-un mediu nu prea bun. Eu însumi înțeleg și simt că colegii mei cu experiență de lucru similară, dar în același Symfony, au o perspectivă mai largă, iar acesta este un efect secundar foarte puternic al Bitrix.
Toate acestea sugerează că, dacă doriți să vă dezvoltați în lumea dezvoltării web, atunci cu siguranță nu ar trebui să alegeți Bitrix ca bază.

Comparând cei doi dezvoltatori, vreau să atrag atenția asupra cadrului în care ne obligă sistemul și libertatea pe care o oferă. Atât Bitrix, cât și Symfony - ambele oferă o flexibilitate aproape nelimitată și, în principiu, pe fiecare dintre ele puteți crea un produs de absolut orice complexitate. Cu toate acestea, sistemul ar trebui să ajute dezvoltatorul să rezolve problemele, în loc să pună o spiță în roți. Și aici Bitrix pierde foarte mult.

Marketing

Vreau să spun câteva cuvinte despre asta imediat, pentru că... aceasta este componenta principală a succesului Bitrix.
Putem spune că întregul Bitrix, chiar și documentația pentru dezvoltatori, este impregnat de spiritul de marketing. Chiar și acolo ei scriu că produsul lor este „atât de cool încât este apreciat și respectat de toți partenerii noștri” (dovadă, bloc „Structură”). Bitrix angajează agenți de marketing buni care știu să își prezinte produsul în mod competent. O dată la șase luni, ei organizează conferințe pentru parteneri, unde vorbesc despre ceea ce s-a făcut și care sunt planurile lor. După cum arată practica, aceste planuri nu se împlinesc niciodată la timp și de foarte multe ori versiunile sunt fie incomplete, fie pline de erori.
De exemplu, refactorizarea senzațională a modulului de vânzare, a cărui lansare a fost amânată cu mai mult de un an, și chiar și ultima dată de lansare (23 decembrie 2015) a fost ratată cu 3 luni, iar un nou magazin și BUS (Bitrix) ed. „Site Management”) versiunea 16 a fost lansată abia la sfârșitul lunii martie 2016. Dar, ca urmare, după actualizare, utilizatorii nu numai că nu au primit funcții noi. Utilizatorii au primit un magazin în mare parte inoperabil și o grămadă de cod nou nedocumentat pentru a porni.
Noile instrumente primesc nume atât de puternice pe care toată lumea le știe: site compozit - accelerație x100; blocuri de mare sarcină; Bitrix BigData. De fapt, aceste cuvinte ascund lucruri destul de obișnuite care nu se ridică la nivelul numelui lor.
Și această abordare se vede peste tot, din păcate. Din exterior, produsul arată ca o bomboană pe care ai cumpărat-o, instalată și folosită. Dar dacă te îndepărtezi de livrarea standard cu Bitrix - asta este tot, menținerea funcționalității în timpul actualizărilor se transformă într-un iad.
Cu toate acestea, în primul rând, subiectul marketingului va apărea cel mai probabil în această postare de mai multe ori.

Arhitectură

Timp de zece ani, Bitrix a ajuns cu disperare într-o fundătură. Fiecare caracteristică nouă a produsului a fost lansată în conformitate cu interesele afacerii, fără o dezvoltare adecvată din punct de vedere tehnic. Și, firește, toate acestea au crescut ca un bulgăre de zăpadă.
Dacă te gândești bine, Bitrix nu are arhitectură ca atare. Nu există nici măcar reguli general acceptate, formulate, care să permită să urmeze această arhitectură. În cursul pentru dezvoltatori, în secțiunea Arhitectură de produs, se spune că Bitrix urmează arhitectura MVC și oferă o diagramă:

Vreau să spun imediat că acest MVC este foarte diferit de versiunea clasică. Există o înlocuire foarte puternică a conceptelor aici, de fapt nu există MVC aici, există pur și simplu un fel de divizare abstractă în module, componente și șabloane de componente. Și întregul site este construit din aceste cărămizi. Dar fiecare dintre aceste cărămizi poate prelua sarcini diferite și, prin urmare, sunt strâns interconectate.
Voi încerca să mă uit la fiecare dintre aceste aspecte ale arhitecturii mai detaliat.

M - Model sau API

Îmi este greu să judec API-ul sistemului ca model. Da, API-ul oferă o interfață pentru accesarea datelor și care vă permite să le manipulați. Dar API-ul Bitrix vă permite să lucrați nu numai cu date, ci și cu șabloane și, de asemenea, cu solicitările utilizatorilor. Ei bine... asta e doar parerea mea.
În acest moment, Bitrix are 2 opțiuni API. În mod convențional, ele pot fi împărțite în vechiȘi nou. Noul API se numește D7 (sincer, nu-mi amintesc de ce, dar Rizhikov a vorbit despre asta la una dintre conferințele partenerilor).

Vechiul API este o colecție de antimodeluri, exemple teribile de cod prost. În Bitrix, a fost întotdeauna considerat normal să se apeleze static metodele non-statice și invers, să se solicite stare atunci când este inadecvat. De exemplu, binecunoscutul CIBlockElement::GetList este poate una dintre cele mai frecvent utilizate metode în dezvoltare. Implementarea sa conține peste 500 de linii de cod, folosește globaluri, creează interogări terifiante, colosale și conține cod nerealist, pur și simplu ilizibil, nedocumentat.

Hai sa ne uitam

Funcția GetList($arOrder=array("SORT"=>"ASC"), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array()) ( /* Combinații de filtre: CHECK_PERMISSIONS= „N” - verificați permisiunile utilizatorului curent la infoblock MIN_PERMISSION="R" - când se verifică permisiunile, apoi nivelul de acces minim SHOW_HISTORY="N" - adăugați elemente din istoric la lista SHOW_NEW="N" - dacă nu adăugați elemente din istoric, apoi adăugați elemente noi, dar nepublicate */ 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"="EL_PARENT_ID", "WF_STATUS_ID" =>"BE.WF_PARENT_ELEMENT_ID", "WF_LAST_HISTORY_ID" =>"BE.WF_LAST_HISTORY_ID", "WF_NEW" =>"BE.WF_NEW", "LOCK_STATUS" => "dacă (BE.WF_DATE_LOCK este nul, "verde", dacă ( DATE_ADD(BE.WF_DATE_LOCK, interval „.$MAX_LOCK.” MINUTE) „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(„AAAA.LL.DD”, „BE.DATE_CREATE”), „BP_PUBLISHED” => „dacă (BE. WF_STATUS_ID = 1, "Y", "N")",); unset($shortFormatActiveDates); unset($formatActiveDates); $bDistinct = fals; CIBlockElement::PrepareGetList($arIblockElementFields, $arJoinProps, $bOnlyCount, $bDistinct, $arSelectFields, $sSelect, $arAddSelectFields, $arFilter, $sWhere, $sSectionWhere, $arAddBy, $SarGup, $arAddSelectFields, $ArAddSelectFields, $SarGup qlComandă, $arAddOrderByFields, $arIBlockFilter, $arIBlockMultProps, $arIBlockConvProps, $arIBlockAllProps, $arIBlockNumProps, $arIBlockLongProps); $arFilterIBlocks = isset($arFilter["IBLOCK_ID"])? array($arFilter["IBLOCK_ID"]): matrice(); //****************DIN PARTEA**************************** ** ************** $sFrom = ""; foreach($arJoinProps["FPS"] as $iblock_id => $iPropCnt) ( $sFrom .= "tttINNER JOIN b_iblock_element_prop_s".$iblock_id." FPS".$iPropCnt." ON FPS".$iPropCnt.".IBLOCK_ELEMENT_ID = BE.IDn"; $arFilterIBlocks[$iblock_id] = $iblock_id; ) foreach($arJoinProps["FP"] ca $propID => $db_prop) ( $i = $db_prop["CNT"]; if($db_prop[ "bFullJoin"]) $sFrom .= "tttINNER 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 .= "tttLEFT 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 = adevărat; if($db_prop["VERSIUNEA"]==2 ) $ strTable = "b_iblock_element_prop_m".$db_prop["IBLOCK_ID"]; else $strTable = "b_iblock_element_property"; if($db_prop["bFullJoin"]) $sFrom .= "tttINNER JOIN ".$strTable." FPV". $i ." ON FPV".$i.".IBLOCK_PROPERTY_ID = FP".$db_prop["JOIN"].".ID AND FPV".$i.".IBLOCK_ELEMENT_ID = BE.IDn"; else $sFrom .= "tttLEFT JOIN ".$strTable." FPV".$i." ON FPV".$i.".IBLOCK_PROPERTY_ID = FP".$db_prop["JOIN"].".ID AND FPV".$i.". IBLOCK_ELEMENT_ID = BE.IDn"; if($db_prop["IBLOCK_ID"]) $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"]; ) foreach($arJoinProps["FPEN"] ca $propID => $db_prop) ( $i = $db_prop["CNT"]; if($db_prop["VERSION"] == 2 && $db_prop["MULTIPLE"] == "N") ( if($db_prop[" bFullJoin" ]) $sFrom .= "tttINNER 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.".IDn"; else $sFrom .= "tttLEFT 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.".IDn"; ) else ( if($db_prop["bFullJoin"]) $sFrom .= "tttINNER 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.".IDn"; else $sFrom .= "tttLEFT 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.".IDn"; ) dacă ($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 .= "tttLEFT 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 ESTE NULL)".($arFilter["SHOW_NEW"]=="Y"? " SAU BE.WF_NEW="Y"": "").")": "")."n"; dacă ( $db_prop["bJoinIBlock"]) $sFrom .= "tttLEFT JOIN b_iblock B".$i." ON B".$i.".ID = BE".$i.".IBLOCK_IDn"; if($db_prop [ „bJoinSection”]) $sFrom . = "tttLEFT JOIN b_iblock_section BS".$i." ON BS".$i.".ID = BE".$i.".IBLOCK_SECTION_IDn"; if($db_prop["IBLOCK_ID"]) $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"]; ) foreach($arJoinProps["BE_FPS"] as $iblock_id => $db_prop) ( $sFrom .= "tttLEFT JOIN b_iblock_element_prop_s".$iblock_id." JFPS".$db_prop["CNT"]." ON JFPS".$ db_prop["CNT"].".IBLOCK_ELEMENT_ID = BE".$db_prop["JOIN"].".IDn"; if($db_prop["IBLOCK_ID"]) $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $ db_prop["IBLOCK_ID"]; ) foreach($arJoinProps["BE_FP"] ca $propID => $db_prop) ( $i = $db_prop["CNT"]; list($propID, $link) = explode("~ ", $propID, 2); if($db_prop["bFullJoin"]) $sFrom .= "tttINNER JOIN b_iblock_property JFP".$i." ON JFP".$i.".IBLOCK_ID = BE".$db_prop[ "JOIN"].".IBLOCK_ID ȘI ". (IntVal($propID)>0? " JFP".$i.".ID=".IntVal($propID)."n": "JFP".$i. ".CODE="".$DB->ForSQL($propID, 200).""n"); else $sFrom .= "tttLEFT 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 = adevărat; 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 .= "tttINNER 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"].".IDn"; else $sFrom .= "tttLEFT 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"].".IDn"; if($db_prop["IBLOCK_ID"]) $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"]; ) foreach($arJoinProps[ "BE_FPEN "] ca $propID => $db_prop) ( $i = $db_prop["CNT"]; list($propID, $link) = explode("~", $propID, 2); if($db_prop[ "VERSIUNEA "] == 2 && $db_prop["MULTIPLE"] == "N") ( if($db_prop["bFullJoin"]) $sFrom .= "tttINNER 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. „.IDn”; else $sFrom . = "tttLEFT 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.".IDn"; ) else ( if($db_prop["bFullJoin"]) $sFrom .= "tttINNER 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.".IDn"; else $sFrom .= "tttLEFT 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.".IDn"; ) dacă ($db_prop["IBLOCK_ID"]) $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"]; ) if(strlen($arJoinProps["BES"])) ( $sFrom .= "ttt".$arJoinProps["BES"]."n"; ) if(strlen($arJoinProps["FC"])) ( $sFrom .= "ttt".$arJoinProps["FC"]."n"; $bDistinct = $bDistinct || (isset($arJoinProps["FC_DISTINCT"]) && $arJoinProps["FC_DISTINCT"] == "Y "); ) if($arJoinProps["RV"]) $sFrom .= "tttLEFT JOIN b_rating_voting RV ON RV.ENTITY_TYPE_ID = "IBLOCK_ELEMENT" AND RV.ENTITY_ID = BE.IDn"; if($arJoinProps["RVU"]) $sFrom .= "tttLEFT 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 .= "ttt".($arJoinProps["RVV"]["bFullJoin"]? "INNER": "LEFT")." JOIN b_rating_vote RVV ON RVV.ENTITY_TYPE_ID = „IBLOCK_ELEMENT” ȘI RVV.ENTITY_ID = BE.IDn”; //********************SFÂRȘIT DIN PARTEA**************************** ********* ****************** $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 "]." "; $sFrom .= str_replace("LEFT JOIN", "ntttLEFT JOIN", $res_catalog["FROM"])."n"; //$sWhere .= $res_catalog["WHERE"]." "; mutat la MkFilter if(is_array($res_catalog["ORDER"]) && count($res_catalog["ORDER"])) ( $bCatalogSort = true; foreach($res_catalog["ORDER"] ca $i=>$ val) $arSqlOrder[$i] = $val; ) ) ) $i = array_search("CREATED_BY_FORMATTED", $arSelectFields); dacă ($i !== fals) ( dacă ($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]); )B) $"Or"; foreach($arSqlOrder ca $i=>$val) ( if(strlen($val)) ( if($sOrderBy=="") $sOrderBy = " ORDERBY "; else $sOrderBy .= ","; $sOrderBy .= $val." "; ) ) $sSelect = trim($sSelect, ", tnr"); 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, "tn") .(in_array("USER_NAME", $arSelectFields)? "tttLEFT JOIN b_user U ON U.ID=BE.MODIFIED_BYn": "") .(in_array("LOCKED_USER_NAME", $arSelectFields)? "tttLEFT JOIN b_user UL ON UL.ID=BE.WF_LOCKED_BYn": "") .(in_array("CREATED_USER_NAME", $arSelectFields) || in_array("CREATED_BY_FORMATTED", $arSelectFields)? "tttLEFT JOIN b_user UC ON UC.ID=BE.CREATED_BYn": "")." "; $strSql = " FROM ".$sFrom." WHERE 1=1 " .$sWhere." ".$sGroupBy." "; if(isset($this) && is_object($this) && isset($this->strField)) ( $this->sFrom = $sFrom; $this->sWhere = $sWhere; returnează "SELECT ".$sSelect.$strSql; ) if($bOnlyCount) ( $res = $DB->Query(" SELECTAȚI „.$sSelect.$strSql, false, „FIȘIER: „.__FILE__”.
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->Interogare(" SELECT ".$sSelect." ,@rank_r:=@rank_r+1 AS rang1 ,dacă (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 între @rank_e-$nPageSize și @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"").") ca 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 = CDBResult nou(); $res->NavQuery($strSql, $cnt, $arNavStartParams); ) ) else//if(is_array($arNavStartParams))) ( $strSql = „SELECT ".$sSelect.$strSql.$sOrderBy; $res = $DB->Query($strSql, false, "FIȘIER: ".__FILE__."
LINE: ".__LINE__); ) $res = new CIBlockResult($res); $res->SetIBlockTag($arFilterIBlocks); $res->arIBlockMultProps = $arIBlockMultProps; $res->arIBlockConvProps = $arIBlockConvProps; $res-> arIBlockAllProps = $arIBlockAllProps; $res->arIBlockNumProps = $arIBlockNumProps; $res->arIBlockLongProps = $arIBlockLongProps; returnează $res; )

După cum ați putea ghici, această metodă primește o listă de elemente de bloc de informații din baza de date, iar pentru a obține lista nu necesită crearea unei instanțe a clasei CIBlockElement. Cu toate acestea, pentru a adăuga un element de bloc de informații, este necesară o stare și numai pentru a înregistra informații despre ultima eroare care a apărut într-o proprietate publică a clasei.

Vechea API folosește intens variabile globale, cum ar fi $APPLICATION, $USER, $DB. Sunt exemple ale anumitor clase, iar în documentație se numeau cu mândrie singletons, deși acum nu am mai găsit aceste cuvinte.
Pentru a genera o eroare, de exemplu, în handlerele de evenimente, trebuie să utilizați metoda $APPLICATION->ThrowException(), care de fapt nu aruncă excepții.

Funcția publică ThrowException($msg, $id = false) ( $this->ResetException(); if(is_object($msg) && (is_subclass_of($msg, "CApplicationException")) || (strtolower(get_class($msg)) =="capplicationexception"))) $this->LAST_ERROR = $msg; altfel $this->LAST_ERROR = noua CApplicationException ($msg, $id); )

Și da - toată această frumusețe este încă folosită în dezvoltarea de noi proiecte, pentru că... D7 nu acceptă încă toate caracteristicile vechiului API. Același modul infoblock vă permite în continuare să efectuați doar o selecție de entități și nu în întregime. Nu este încă posibil să creați un element nou sau să actualizați unul existent utilizând noul API.

Noul API este deja oarecum diferit de cel vechi. În primul rând, tot codul din noul nucleu este împărțit în spații de nume, unde există o dependență clară de modul. De exemplu, analogul CIBlockElement::GetList din noul nucleu este BitrixIblockElementTable::getList, unde spațiul de nume rădăcină este numele furnizorului, iar spațiul de nume următor este numele modulului. Pentru ca acest lucru să funcționeze, Bitrix a scris propriul său autoloader, BitrixMainLoader::autoLoad, care nu este deloc compatibil cu PSR-0/4.

De fapt, codul de încărcare automată sub forma unei singure funcții

Funcția publică statică autoLoad($className) ( $fișier = ltrim($className, "\"); // remediați web env $file = strtr($fișier, static::ALPHA_UPPER, static::ALPHA_LOWER); static $documentRoot = null; if ($documentRoot === null) $documentRoot = static::getDocumentRoot(); if (isset(self::$arAutoLoadClasses[$fișier])) ( $pathInfo = self::$arAutoLoadClasses[$fișier]; if ($pathInfo["modul"] != "") ( $m = $pathInfo["modul"]; $h = isset(self::$arLoadedModulesHolders[$m]) ? self::$arLoadedModulesHolders[$m ]: "bitrix"; include_once($documentRoot."/".$h."/modules/".$m."/" .$pathInfo["fișier"]); ) else ( require_once($documentRoot.$pathInfo) ["fișier"]); ) return; ) if (preg_match("#[^\\/a-zA-Z0-9_]#", $fișier)) return; if (substr($fișier, -5) = = "tabel") $fișier = substr($fișier, 0, -5); $fișier = str_replace ("\", "/", $fișier); $arFișier = explode ("/", $fișier); dacă ($arFile === „bitrix”) ( array_shift($arFile); if (gol ($arFile)) return; $modul = array_shift($arFile); dacă ($modul == null || gol($arFile) ) întoarcere; ) else ( $modul1 = array_shift($arFile); $modul2 = array_shift($arFile); if ($modul1 == null || $modul2 == null || empty($arFile)) return; $modul = $modul1 .".".$modul2; ) dacă (!isset(self::$arLoadedModulesHolders[$modul])) returnează; $filePath = $documentRoot."/".self::$arLoadedModulesHolders[$module].."/modules/".$modul."/lib/".implode("/", $arFile).".php" ; if (file_exists($filePath)) require_once($filePath); )

Noul API arată multă dragoste pentru Singleton:

  • BitrixMainApplication::getInstance - instanță de aplicație
  • BitrixMainConfigConfiguration::getInstance - instanță de clasă pentru gestionarea configurațiilor
  • BitrixMainPageAsset::getInstance - Instanță manager de active
  • BitrixMainEventManager::getInstance - manager de evenimente

Poate că în viitor toate acestea vor dobândi propriul ServiceLayer (există un anume BitrixMainServiceManager în noul nucleu, care nu este încă folosit și nu este documentat).Dar încă sunt puține speranțe.

ORM este o altă inovație a lui D7 și acesta este ceva ce poate pretinde a fi un model real! Puteți distinge o clasă de entitate ORM de orice altă clasă după numele ei. O clasă de entitate trebuie să se termine întotdeauna cu Table (ElementTable, SectionTable, OrderTable etc.). Mai mult, în mod paradoxal, numele fișierului cu clasa de entitate ORM nu ar trebui să se termine în Tabel. De exemplu, pentru ElementTable trebuie să creăm un fișier element.php. Captura de ecran de mai jos arată conținutul directorului lib (încărcarea automată D7 funcționează numai în acest director) al modulului iblock. Încercați să determinați ochi care sunt entitățile ORM și care sunt clasele obișnuite cu logica de afaceri.


ORM, în general, nu este încă nimic special. Vă permite să descrieți tabele de baze de date sub formă de clase și vă permite să executați interogări pe aceste tabele și să le conectați între ele. Nu există ActiveRecord sau Repository și nu este de așteptat.

Un exemplu de clasă de entitate ORM tipică pentru un element infoblock

*

  • ID int obligatoriu *
  • TIMESTAMP_X datetime opțional *
  • MODIFIED_BY int opțional *
  • DATE_CREATE datetime opțional *
  • CREATED_BY int opțional *
  • IBLOCK_ID int obligatoriu *
  • IBLOCK_SECTION_ID int opțional *
  • ACTIV bool opțional implicit „Y” *
  • ACTIVE_FROM datetime opțional *
  • ACTIVE_TO datetime opțional *
  • SORT int opțional implicit 500 *
  • șir NAME (255) obligatoriu *
  • PREVIEW_PICTURE int opțional *
  • PREVIEW_TEXT șir opțional *
  • PREVIEW_TEXT_TYPE enumerare ("text", "html") opțional implicit "text" *
  • DETAIL_PICTURE int opțional *
  • șir DETAIL_TEXT opțional *
  • DETAIL_TEXT_TYPE enumerare ("text", "html") opțional implicit "text" *
  • șir SEARCHABLE_CONTENT opțional *
  • WF_STATUS_ID int opțional implicit 1 *
  • WF_PARENT_ELEMENT_ID int opțional *
  • WF_NEW enumerare ("N", "Y") opțional *
  • WF_LOCKED_BY int opțional *
  • WF_DATE_LOCK datetime opțional *
  • șir WF_COMMENTS opțional *
  • IN_SECTIONS bool opțional implicit „N” *
  • șir XML_ID (255) opțional *
  • CODE șir (255) opțional *
  • TAGS șir (255) opțional *
  • TMP_ID șir (40) opțional *
  • WF_LAST_HISTORY_ID int opțional *
  • SHOW_COUNTER int opțional *
  • SHOW_COUNTER_START opțional*
  • PREVIEW_PICTURE_FILE referință la (@link BitrixFileFileTable) *
  • DETAIL_PICTURE_FILE referință la (@link BitrixFileFileTable) *
  • Referință IBLOCK la (@link BitrixIblockIblockTable) *
  • Referință WF_PARENT_ELEMENT la (@link BitrixIblockIblockElementTable) *
  • referință IBLOCK_SECTION la (@link BitrixIblockIblockSectionTable) *
  • referință MODIFIED_BY_USER la (@link BitrixUserUserTable) *
  • CREATED_BY_USER referință la (@link BitrixUserUserTable) *
  • Referință WF_LOCKED_BY_USER la (@link BitrixUserUserTable) * * * @pachet BitrixIblock **/ clasa ElementTable extinde MainEntityDataManager ( const TYPE_TEXT = "text"; const TYPE_HTML = "html"; /** * Returnează numele tabelului DB pentru entitate. * * @return șir */ funcția publică statică getTableName( ) ( returnează "b_iblock_element"; ) /** * Returnează definiția hărții entității. * * @return array */ funcție publică statică getMap() ( return array("ID" => new MainEntityIntegerField("ID", array("primary") " => adevărat, "completare automată" => adevărat, "titlu" => Loc::getMessage("ELEMENT_ENTITY_ID_FIELD"),)), "TIMESTAMP_X" => new MainEntityDatetimeField ("TIMESTAMP_X", array("default_value" => new MainTypeDateTime(), "title" => Loc::getMessage("ELEMENT_ENTITY_TIMESTAMP_X_FIELD"),)), "MODIFIED_BY" => new MainEntityIntegerField("MODIFIED_BY", array("title" => Loc::getMessage("ELEMENT_ENTITY_MODIFIED_BY_FIELD")) ,)), "DATE_CREATE" => new MainEntityDatetimeField ("DATE_CREATE", array ("default_value" => new MainTypeDateTime(), "title" => Loc::getMessage ("ELEMENT_ENTITY_DATE_CREATE_FIELD"),)), "CREATED_BY" = > new MainEntityIntegerField("CREATED_BY", array("title" => Loc::getMessage("ELEMENT_ENTITY_CREATED_BY_FIELD")),)), "IBLOCK_ID" => new MainEntityIntegerField("IBLOCK_ID", array("required" => true, " title" => Loc::getMessage("ELEMENT_ENTITY_IBLOCK_ID_FIELD"),)), "IBLOCK_SECTION_ID" => new MainEntityIntegerField("IBLOCK_SECTION_ID", array("title" => Loc::getMessage("ELEMENT_ENTITY_IBLOCK_SECTION_SECTION"),)_ID "FIELD") ACTIVE" => new MainEntityBooleanField("ACTIVE", array("values" => array("N", "Y"), "default_value" => "Y", "title" => Loc::getMessage("ELEMENT_ENTITY_ACTIVE_FIELD" "),)), "ACTIVE_FROM" => new MainEntityDatetimeField("ACTIVE_FROM", array("title" => Loc::getMessage("ELEMENT_ENTITY_ACTIVE_FROM_FIELD")),)), "ACTIVE_TO" => new MainEntityDatetimeField("ACTIVE_TO", array("title" => Loc::getMessage("ELEMENT_ENTITY_ACTIVE_TO_FIELD"),)), "SORT" => new MainEntityIntegerField ("SORT", array("default_value" => 500, "title" => Loc::getMessage ("ELEMENT_ENTITY_SORT_FIELD"),)), "NAME" => nou MainEntityStringField ("NAME", array ("necesar" => true, "validation" => array(__CLASS__, "validateName"), "title" => Loc ::getMessage("ELEMENT_ENTITY_NAME_FIELD"),)), "PREVIEW_PICTURE" => new MainEntityIntegerField("PREVIEW_PICTURE", array("title" => Loc::getMessage("ELEMENT_ENTITY_PREVIEW_PICTURE_FIELD")),)), "PREVIEW_TEXT" => nou MainEntityTextField("PREVIEW_TEXT", array("title" => Loc::getMessage("ELEMENT_ENTITY_PREVIEW_TEXT_FIELD")),)), "PREVIEW_TEXT_TYPE" => new MainEntityEnumField("PREVIEW_TEXT_TYPE", array("valori" => array(self:: TYPE_TEXT, self::TYPE_HTML), "default_value" => self::TYPE_TEXT, "title" => Loc::getMessage ("ELEMENT_ENTITY_PREVIEW_TEXT_TYPE_FIELD"),)), "DETAIL_PICTURE" => nou MainEntityIntegerField ("DETAIL_PICTURE", matrice( "title" => Loc::getMessage("ELEMENT_ENTITY_DETAIL_PICTURE_FIELD"),)), "DETAIL_TEXT" => nou MainEntityTextField ("DETAIL_TEXT", array("title" => Loc::getMessage("ELEMENT_ENTITY_DETAIL_TEXT_FIELD")),)), "DETAIL_TEXT_TYPE" => new MainEntityEnumField ("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 MainEntityTextField("SEARCHABLE_CONTENT", array("title" => Loc::getMessage("ELEMENT_ENTITY_SEARCHABLE_CONTENT_FIELD"),)), "WF_STATUSFintID"("WF_STATUSEeldity") "Wf_status_id", Array ("Titlu" => Loc :: getmessage ("Element_entity_wf_status_id_field"),)), "WF_PARENT_ELEMENT_ID" => New MainentityintegerField , Array ("Title" => Loc :: Getmessage ("Element_entity_wf_part_field "),)_id_field ), "WF_NEW" => new MainEntityEnumField ("WF_NEW", array("values" => array ("N", "Y"), "title" => Loc::getMessage ("ELEMENT_ENTITY_WF_NEW_FIELD") ,)), "WF_LOCKED_BY" => MainEntityIntegerField nou ("WF_LOCKED_BY", array("title" => Loc::getMessage("ELEMENT_ENTITY_WF_LOCKED_BY_FIELD")),)), "WF_DATE_LOCK" => nou MainEntityDatetimeField("WF_LOCKED",_D) > Loc::getMessage("ELEMENT_ENTITY_WF_DATE_LOCK_FIELD"),)), "WF_COMMENTS" => new MainEntityTextField("WF_COMMENTS", array("title" => Loc::getMessage("ELEMENT_ENTITY_WF_COMMENTS_FIELD")),)", = "IN_SECTIONS > new MainEntityBooleanField("IN_SECTIONS", array("values" => array("N", "Y"), "title" => Loc::getMessage("ELEMENT_ENTITY_IN_SECTIONS_FIELD"),)), "XML_ID " => nou MainEntityStringField("XML_ID", array("validation" => array(__CLASS__, "validateXmlId"), "title" => Loc::getMessage("ELEMENT_ENTITY_XML_ID_FIELD"),)), "CODE" => nou MainEntityStringField("CODE ", array("validation" => array(__CLASS__, "validateCode"), "title" => Loc::getMessage("ELEMENT_ENTITY_CODE_FIELD"),)), "TAGS" => new MainEntityStringField("TAGS ", array( "validation" => array(__CLASS__, "validateTags"), "title" => Loc::getMessage("ELEMENT_ENTITY_TAGS_FIELD"), "TMP_ID" => new MainEntityStringField ("TMP_ID", array( "validation" => array( __CLASS__, „va

    Și un exemplu de lucru cu această entitate

    //Selectarea datelor $dbElements = BitrixIblockElementTable::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["DATA_ACTIVE_FROM"]->format("d.m.Y H:i:s"); ) //Adăugarea unei înregistrări $addResult = BitrixIblockElementTable::add([ "NAME" => "Numele noului element", "IBLOCK_ID" => CATALOG_IBLOCK_ID ]); dacă (!$addResult->isSuccess()) ( echo implode("
    " ,$addResult->getErrorMessages()); )

    Bitrix este foarte mândru de modul său Highload Blocks, care este scris în întregime folosind D7.
    Anterior, aveau doar blocuri de informații ca stocare pentru un set arbitrar de informații. Un bloc de informații, pentru cei care nu știu, este o entitate care este stocată în baza de date ca un complex de mai multe tabele (1 tabel pentru câmpurile „de bază” ale unui element de bloc de informații și până la 2 tabele pentru proprietățile unui element de bloc informativ). Toate câmpurile elementului de bază toata lumea infoblocurile sunt stocate într-un singur tabel. Dacă aveți 15 blocuri de informații, fiecare dintre ele având 500.000 de elemente, toate aceste elemente vor fi de fapt într-un singur tabel. Proprietăți suplimentare ale elementelor infobloc sunt unite din alte tabele. Dacă acestea sunt blocuri de informații din prima versiune, atunci toate proprietățile tuturor blocurilor de informații sunt, de asemenea, într-un singur tabel, iar în cazul blocurilor de informații 2.0 (bună ziua, marketing) - proprietățile fiecărui bloc de informații sunt deja împărțite în tabele diferite .
    Și toată această chestiune a încetinit deja foarte mult pe seturi de date relativ mici. 400.000 de elemente dintr-un singur bloc de informații încetinesc deja activitatea panoului de administrare. Marketerii de la Bitrix s-au gândit și au creat blocuri Highload! Diferența de implementare între blocurile de informații convenționale este minimă. Acum, pentru fiecare bloc de încărcare mare, este creat propriul său tabel + în plus, este creat un alt tabel pentru a stoca mai multe valori. Ei au numit abordarea obișnuită pentru crearea unui tabel obișnuit într-o bază de date un nume mândru încărcătură mare pur și simplu pentru că încetinește mai puțin decât blocurile de informații obișnuite!
    În plus, în interiorul modulului, pentru ca acesta să funcționeze conform D7, clasele de entități sunt generate dinamic și evaluate la fiecare hit. Aceasta este încărcarea mare.

    Uita-te la asta

    Funcția publică statică compileEntity($hlblock) ( global $USER_FIELD_MANAGER; // generează entitate și manager de date $fieldsMap = array(); // adaugă ID $fieldsMap["ID"] = array("data_type" => "integer", „primar” => adevărat, „completare automată” => adevărat); // construiește clasa datamanager $entity_name = $hlblock["NAME"]; $entity_data_class = $hlblock["NAME"]; if (!preg_match("/^ +$/i", $entity_data_class)) ( aruncă o nouă excepție MainSystemException(sprintf("Nume nevalid al entității `%s`.", $entity_data_class)); ) $entity_data_class .= "Tabel"; dacă (class_exists($entity_data_class)) ( // reconstruiți dacă există deja EntityBase::destroy($entity_data_class); ) else ( $entity_table_name = $hlblock["TABLE_NAME"]; // faceți cu o hartă goală $eval = " class ".$entity_data_class. " extins ".__NAMESPACE__."DataManager ( funcția publică statică getTableName() ( returnează ".var_export($entity_table_name, true)."; ) funcția publică statică getMap() ( returnează ".var_export($fieldsMap, true)."; ) funcția publică statică getHighloadBlock() ( returnează „.var_export($hlblock, true).”; ) ) "; eval($eval); ) // apoi configurați și atașați câmpurile /** @var BitrixMainEntityDataManager $entity_data_class */ $entity = $entity_data_class::getEntity(); $uFields = $USER_FIELD_MANAGER->getUserFields("HLBLOCK_" ".$hlblock["ID"]); foreach ($uFields ca $uField) ( dacă ($uField["MULTIPLE"] == "N") ( // doar adăugați un singur câmp $field = $USER_FIELD_MANAGER->getEntityField ($uField, $uField["FIELD_NAME"]); $entity->addField($field); foreach ($USER_FIELD_MANAGER->getEntityReferences($uField, $field) ca $referință) ( $entity->addField($reference) ); ) ) else ( // build utm entity static::compileUtmEntity($entity, $uField); ) ) return EntityBase::getInstance($entity_name); )

    La naiba cu asta, dar aceleași blocuri de mare încărcare nu pot acționa în niciun fel ca o alternativă la blocurile de informații obișnuite. Se pare că au fost inventate doar pentru a stoca date de referință non-ierarhice. În plus, modulul încă nu acceptă astfel de funcții necesare în panoul de administrare, cum ar fi filtrarea după un câmp precum „Data”; este imposibil să apelați entitatea HLblock cu un nume ușor de înțeles de către om, astfel încât administratorul să nu se sperie de fiecare dată când intri în pagina de editare a entității, de exemplu, BrandReference. Toate acestea sugerează că acest lucru a fost conceput ca o alternativă la blocurile de informații lente, dar nu au avut timp să-l termine (sau nu l-au gestionat, sau a mers împotriva intereselor afacerii), iar în final au lansat funcționalitatea semifinisată ca o caracteristică nouă, iar marketerii l-au pieptănat și l-au făcut să arate frumos au venit cu această idee.

    C - Controler sau componentă

    O componentă obișnuită în Bitrix poate fi comparată cu widget-urile din Yii. Acesta este un anumit container, separat de toate celelalte containere, care ia ca intrare unii parametri, lucrează și, cu rezultatul muncii, conectează vizualizarea. Dezvoltatorii Bitrix sunt profund convinși că componentele pe care le furnizează imediat rezolvă majoritatea problemelor cu care se confruntă colegii lor. Dar, ca de obicei, dezvoltatorilor nu le place întotdeauna nimic, iar capacitățile componentelor standard sunt întotdeauna „puțin” lipsite. Prin urmare, Bitrixoids a decis să ofere dezvoltatorilor posibilitatea de a modifica rezultatul unei componente... folosind o vizualizare. În directorul șablon de componente, puteți crea un fișier result_modifier.php, în care puteți adăuga propriile date la rezultatul componentei. Și dacă dintr-o dată doriți să utilizați aceste date într-un alt șablon, va trebui să copiați și să lipiți acest fișier (sau să includeți acest fișier dintr-un alt șablon). Mereu am fost chinuit de întrebarea - pentru ce este acest patos? De ce să nu adăugați o grămadă de solicitări direct în șablonul PHP? Diferența se dovedește a fi mică.
    Despre ce vorbesc șabloane în secțiunea de controlere...

    Bitrix are 2 tipuri de componente 2.0 (hello marketing again) - obișnuite și complexe. O componentă comună este un widget. O componentă complexă este un fel de controler + router, care, pe baza adresei URL, înțelege ce pagină cu un set de widget-uri trebuie afișată. Procedura de operare este aproximativ aceasta:

    • url-ul spune /catalog/bolshaya-zelenaya-shapka.html
    • folosind mod_rewrite, Bitrix înțelege că pentru secțiunea fizică /catalog trebuie să includeți întotdeauna fișierul /catalog/index.php
    • componenta complexă analizează adresa URL și înțelege că trebuie să conectați o pagină de produs detaliată, să o numim detaliu
    • o componentă complexă colectează parametrii necesari pentru funcționarea componentelor sale fii
    • o componentă complexă își conectează șablonul detail.php, în interiorul căruia este specificată conexiunea componentelor obișnuite copil

    Nu arată foarte frumos, dar poate funcționa. Totuși, nu totul este atât de simplu... Dacă modificați parametrii unei componente complexe folosind un editor vizual, fișierul cu setările de adresare (urlrewrite.php) va fi suprascris de sistem. Mai mult, dacă dintr-o dată ai scris ceva greșit acolo pentru alte pagini, cu siguranță ceva se va rupe fără niciun avertisment. În practică, acest lucru poate duce la pierderea funcționalității secțiunilor întregi ale site-ului.
    Setarea parametrilor unei componente complexe poate fi o corvoadă. O astfel de componentă poate avea cu ușurință sute de parametri de intrare, pur și simplu pentru că este necesară configurarea parametrilor componentelor copil.
    O componentă complexă - pare a fi un router. Cu toate acestea, toate rutele pe care le creați în această componentă nu vor fi incluse în sitemap.xml generat automat. Aceste link-uri nu vor fi incluse în modulul de căutare. Nu veți avea nicio modalitate de a genera adresa către traseu din exterior (de exemplu, doriți să puneți un link către pagina detaliată a unei mărci undeva în bara laterală și nu veți putea cere routerului să genereze această adresă URL ).

    În general, nimeni nu realizează cu adevărat funcțiile unui router în Bitrix. În infoblocks, puteți configura șablonul de adresă URL pentru pagina infoblock, pagina secțiunii infoblock și pagina element infoblock. Gata, infoblocurile nu mai pot avea pagini.
    Pentru forumuri, este posibil să personalizați șabloanele unor pagini. Poate fi personalizat pentru bloguri. Poate că ceva poate fi configurat în altă parte... toate acestea sunt atât de descentralizate încât devine destul de dificil să le adunăm pe toate.

    Componentele obișnuite sunt entități puțin mai simple decât componentele complexe. Sarcina lor este să preia un set de parametri ca intrare, să-i proceseze, să transmită rezultatul muncii în șablon și să memoreze totul în cache.
    Toată logica componentelor este conținută în fișierul component.php. Cu versiunea 12 a Bitrix (în prezent versiunea 16 este actuală, au trecut 4 ani), a devenit posibilă „utilizarea OOP” în componente. Această inovație este că în locul unui fișier component.php, puteți crea un fișier class.php, în care, în locul tăițeilor obișnuiți, puteți scrie o clasă moștenită de la CBitrixComponent. Și acesta a fost un mare pas înainte, pentru că... a devenit posibil să moștenești componente și să nu folosești deloc result_modifier.php și să nu exersezi copy-paste dacă dintr-o dată trebuie să personalizi foarte mult componenta. Dar nici aici totul nu este încă atât de bine. Din întregul set de componente, doar 25-30 la sută se pot lăuda că au o clasă în arsenalul lor. Mai mult decât atât, o jumătate bună dintre ele pur și simplu nu vă vor oferi posibilitatea de a vă extinde complet, deoarece... Ele sunt adesea scrise ilogic.
    Apropo, oamenii buni încearcă să standardizeze, să ajute cumva dezvoltatorii să scrie componente și există un set de instrumente corespunzător

    V - Vizualizare sau șabloane

    Șabloanele din Bitrix pot fi împărțite în mai multe tipuri:

    • Șabloane de componente 2.0 obișnuite și complexe
    • Șabloane de site-uri web
    • Șabloane pentru alte entități (mailing-uri, buletine informative, formulare web, generatoare de export și multe altele)

    Șabloanele de componente au chiar și capacitatea de a utiliza motoare de șabloane. În principiu, puteți conecta orice motor șablon, dar nu există instrumente auxiliare din cutie. Dacă cineva are nevoie de el, am câteva link-uri către extensii pentru crenguță și lamă, care funcționează și sunt destul de folosite în producție. Dar chiar și aici Bitrixoizii au devenit pervertiți. Motorul de șablon poate fi utilizat numai cu componente. Nu va fi posibilă conectarea motorului de șabloane la redarea șablonului site-ului web sau la alte entități, deoarece acolo nu există un renderer.

    Un alt lucru enervant despre șabloanele componente este plasarea lor. Componenta este conectată folosind un design simplu

    $APPLICATION->IncludeComponent("bitrix:catalog.section", "nume_șablon", );

    Al doilea parametru este numele șablonului componentei. Deci, în funcție de diferite condiții, locația acestui șablon poate fi în locurile cele mai neașteptate:

    • bitrix/components/bitrix/catalog.section/templates/template_name
    • local/components/bitrix/catalog.section/templates/template_name
    • bitrix/templates/.default/components/bitrix/catalog.section/template_name
    • bitrix/templates/site_template/components/bitrix/catalog.section/template_name
    • local/templates/.default/components/bitrix/catalog.section/template_name
    • local/templates/site_template/components/bitrix/catalog.section/template_name
    • bitrix/components/bitrix/catalog/templates/.default/bitrix/catalog.section/template_name
    • local/templates/site_template/components/bitrix/catalog/.default/bitrix/catalog.section/template_name

    Și încă nu am enumerat toate opțiunile...

    Un șablon de site poate fi considerat ca un set de fișiere: header.php, footer.php (da, site-ul trebuie să le aibă), description.php (descrierea de sistem a șablonului de site), template_styles.css (stiluri de șablon de site), director cu șabloane de componente și un alt grup de fișiere mai puțin semnificative. Asta e tot. Și nu o poți influența în niciun fel, nu poți face nimic în privința asta. Este imposibil să ridicați motorul de șablon.

    Nu este nimic de spus despre alte șabloane. Ele sunt fie pur și simplu stocate în baza de date sub forma unui aspect cu niște date „variabile” incluse în ea, fie este un fișier PHP stupid care face toată munca, de la preluarea parametrilor din baza de date până la afișarea informațiilor. De exemplu, vă puteți uita la generatorul de fișiere YML pentru piață. Nu are rost să-l postezi aici, pur și simplu pentru că este destul de mare, cam de 2k de linii. Cine are nevoie de el îl poate căuta pe google, este în /bitrix/modules/catalog/load/yandex_run.php

    Natura fișierului

    După cum a devenit clar mai sus, în Bitrix arhitectura nu este foarte bună. Dar Bitrix are și un alt aspect important al arhitecturii sale.
    Bitrix este un CMS cu jumătate de fișier. Multe lucruri sunt controlate folosind unele fișiere:

    • Aveți nevoie de o pagină - creați un fișier
    • Aveți nevoie de un set de pagini - creați un fișier și conectați acolo o componentă care funcționează cu infoblocks
    • Trebuie să setați un titlu pentru pagină - editați fișierul
    • Trebuie să setați un titlu pentru toate paginile unei secțiuni - creați un fișier special.section.php în rădăcina acestei secțiuni
    • Trebuie să editați drepturile - editați fișierul .access.php
    • Setări înainte de inițializarea sistemului - în fișierul dbconn.php, .settings.php și .settings_extra.php
    • result_modifier.php, component_epilog.php, init.php, .parameters.php, .description.php ....

    Și există un număr mare de astfel de fișiere speciale împrăștiate în Bitrix. Pe de o parte, aceasta oferă o anumită flexibilitate atunci când lucrați cu sistemul. Pe de altă parte, acest lucru se poate transforma în chin atât pentru dezvoltator, cât și pentru managerul site-ului. Fișierele de pagină se transformă uneori într-o mizerie de cod PHP, aspect și componente de plug-in. Drept urmare, editorul vizual poate analiza incorect acest fișier și, atunci când îl editează, poate scăpa cu ușurință de etichetele PHP în unele locuri, ceea ce va duce la nefuncționarea paginii. Spuneți - nu este nevoie să scrieți cod PHP în astfel de fișiere? Da, știu. Dar Bitrix foarte des și fără alternativă te obligă să faci asta.
    Și trebuie să păstrați în mod constant informații în cap despre ce fel de fișiere sunt și ce date pot conține. Fișierele diferite ar trebui să conțină date diferite cu structuri diferite și trebuie să le amintiți pentru fiecare opțiune. Să cauți asta în documentație de fiecare dată este o muncă grea.

    Pe lângă cele de mai sus

    Vă puteți plânge la nesfârșit de cât de prost funcționează totul în Bitrix. În opinia mea, toate aceste plângeri pot fi caracterizate printr-o singură expresie - „cumva nu complet”. Și într-adevăr, dacă Bitrixoids anunță brusc un fel de caracteristică, atunci nu o eliberează cumva complet, nu o termină, nu o aduc în minte. Există o mulțime de exemple:

    • implementat ORM - încă neterminat, nu poate fi utilizat pe deplin
    • Am făcut un autoloader, funcționează doar în module, și nu conform standardelor
    • a făcut posibilă conectarea unui motor de șablon, dar nu îl puteți folosi peste tot și nu complet
    • etc. și așa mai departe.

    Pe scurt, voi încerca să caracterizez problemele rămase cu care trebuie să mă confrunt în fiecare zi.

    Admin

    Dacă cineva a lucrat cu panoul de administrare, și-a creat paginile în partea administrativă așa cum sugerează Bitrix, mă va înțelege. E doar iadul. Pentru cei care nu cunosc, Bitrix sugerează utilizarea unui fișier cu tăiței pentru fiecare pagină. De exemplu, pagina pentru o vizualizare detaliată a unei comenzi în panoul de administrare realizată de dezvoltatorii Bitrix preia 4k linii. IDE-ul meu începe să încetinească la vizualizarea conținutului acestui fișier. Acolo aveți php, js și html. Este bine că au scăpat de SQL, deși sunt sigur că este pe alte pagini administrative.
    Și ce a împiedicat paginile administrative să funcționeze folosind aceleași componente nu este clar. Pur și simplu nu există nicio modalitate de a personaliza majoritatea paginilor administrative. În cazul componentelor, acest lucru ar putea fi făcut în cel mai scurt timp.
    Apropo, oamenii buni au realizat un modul care te va ajuta să construiești pagini administrative

    js framework

    Bitrix are o componentă js care acționează ca un fel de cadru client. Niciunul dintre dezvoltatori nu-i place din mai multe motive:

    • aproape că nu este documentat
    • el este monstruos
    • dublează în mare măsură jquery-ul familiar pentru mulți

    Bitrix îl folosește foarte des în componentele sale, provocând astfel și mai multă furie în rândul dezvoltatorilor. Nucleul acestei biblioteci în formă redusă este de 85 kb, ceea ce nu este mic. Nu puteți evita conectarea acestuia dacă doriți să utilizați toate capabilitățile Bitrix (compozit, gestionarea activelor).

    Spirit de copiere-lipire

    În ultimul timp, din ce în ce mai rar, dar totuși destul de des, Bitrix te obligă să faci copy-paste ceva. Dacă doriți să modificați funcționarea unei componente, copiați și lipiți-o. Dacă doriți să vă creați propriul șablon de încărcare, copiați și inserați cel de sistem și finalizați-l. Dacă doriți să faceți aproape același șablon pe care îl aveți, copiați-l și lipiți-l și schimbați-l puțin. Și chiar vorbesc despre asta în cursurile pentru dezvoltatori începători. Nu am cuvinte.

    Asset-management și CDN

    Îmi place foarte mult modul în care Bitrix gestionează resursele. În principiu, este posibil să se înregistreze un set de „biblioteci” specifice. Fiecare bibliotecă este un set de fișiere css/js, care poate depinde de alte biblioteci. Dacă conectați o bibliotecă la o pagină, înainte de a o conecta, toate dependențele vor fi rezolvate și toate bibliotecile dependente vor fi inserate în pagină. Totul pare să fie în regulă, doar fiecare resursă va fi inserată ca fișier separat în scriptul sau eticheta de link. Și datorită acestui lucru, există site-uri care au 30-50 de scripturi și același număr de fișiere de stil conectate.
    Este o întrebare de rahat, au spus ei în Bitrix și au făcut o bifă magică care combină toate aceste fișiere într-unul singur. Și au apărut site-uri unde în loc de 50 de scripturi erau 2, fiecare de 300-500kb. Cu ceva timp în urmă, această fuziune a funcționat cu erori și a îmbinat aceleași resurse de mai multe ori, dar acum pare să fi fost remediată.
    Și apoi Bitrixoids a ieșit din cale - au adăugat capacitatea de a încărca toate resursele pe un server CDN. care cade mereu...
    Apoi a apărut Google Pagespeed Insights, care a recomandat mutarea tuturor resurselor în partea de jos a paginii. Și în Bitrix au făcut din nou o casetă magică care omite în mod prostesc toate resursele din corp dacă nu sunt marcate cu un atribut special.
    De asemenea, distribuie versiuni reduse ale scripturilor lor împreună cu caseta, care sunt conectate atunci când utilizați o altă casetă de selectare magică în panoul de administrare.
    În general, fără scss pentru tine, fără TypeScript. Dacă doriți să gestionați resursele în mod competent, nu utilizați sistemul Bitrix încorporat, folosiți webpack, care poate fi asociat cu ușurință cu Bitrix.

    Multi-site/multilingv

    Aceasta este probabil cea mai mare durere de cap pentru un dezvoltator, care a avut loc încă de la începutul produsului. Nu puteți să continuați și să creați un site web multilingv. Și dacă aveți nevoie de un catalog multilingv cu diferite prețuri și valute, atunci acesta se transformă în făină, pentru care trebuie să plătiți și o sumă ordonată (va trebui să plătiți pentru achiziționarea unei licențe suplimentare pentru următoarea versiune lingvistică a site-ul).
    Dacă creați un site web multilingv și cu mai multe monede, atunci fiți pregătiți pentru faptul că Bitrix va rezista foarte agresiv. Setările multisite sunt descentralizate în panoul de administrare. Fiecare entitate din panoul de administrare are propria sa dependență de versiunea lingvistică a site-ului. Este posibil ca unele entități să nu accepte deloc dependențe de site/limbă, în timp ce altele au doar o conexiune neechivocă la limbă, așa că această entitate va trebui să fie duplicată și apoi acceptată.
    În versiunea de bază, pentru a face ca un bloc de informații să funcționeze în mai multe limbi, va trebui să creați un duplicat al acestui bloc de informații. Dar, în practică, nimeni nu face acest lucru și încearcă să vină cu propriile modalități de stocare centrală a unei entități, distribuind atributele sale dependente de limbă către alte unități de stocare.
    Nu puteți seta limba implicită în timpul localizării. Dacă aveți o variabilă de limbă care descrie o expresie în rusă, iar această variabilă de limbă nu este în versiunea în limba engleză, atunci o linie goală va fi afișată pe site-ul în limba engleză și aceasta nu poate fi influențată în niciun fel (în multe cazuri ați putea lăsați fraza rusă ca să nu existe goluri).

    Mecanismul de gestionare a drepturilor

    Au fost foarte deștepți cu acest subsistem. Este adesea dificil să vă dați seama de ce ați acordat drepturi de vizualizare unei entități, dar utilizatorul nu le poate folosi. De exemplu, pentru a acorda dreptul de a edita un bloc de informații, trebuie să acordați acces la directorul /bitrix/admin, să acordați drepturi pentru un anumit bloc de informații și să acordați drepturi în modulul principal. Trebuie făcute prea multe operațiuni pentru a emite drepturi pentru o entitate. Și dacă nu există suficiente drepturi, atunci fără a căuta codul sursă nu există nicio modalitate de a înțelege de ce.

    Configurare

    Bitrix nu are un hub centralizat care să vă permită să gestionați setările sistemului. Setările sunt din nou descentralizate în întregul sistem. Opțiunile sunt disponibile în setările modulului, în setările componentelor, în COption (nefiind plasate în panoul de administrare). În panoul de administrare, opțiunile pentru un modul pot fi distribuite pe 3-4 pagini diferite, care sunt situate în locuri complet diferite. urlrewrite poate fi editat prin panoul de administrare! Acum și .settings și .settings_extra. Uneori nu este deloc clar care dintre ele are prioritate mai mare, de foarte multe ori nu există suficiente explicații pentru opțiuni, iar relațiile sunt neclare. Nu există o modalitate nativă de a partaja configurația între dezvoltatori.
    Setările pot fi foarte ilogice. Uneori ajunge până la absurd... uită-te la componenta bigdata - poate o persoană neinstruită să o configureze?

    Integrare cu 1C

    Acesta este elementul de pe lista de caracteristici Bitrix pentru care se îndrăgesc un număr destul de mare de clienți. Bitrix promite să configureze integrarea bidirecțională a site-ului cu 1C în 2 clicuri, care va livra instantaneu conținut și documente de la un sistem la altul.
    Da, chiar este, dar cu mai multe avertismente.
    În primul rând, pentru a face integrarea „din cutie” fără efort suplimentar, trebuie să faceți totul exact așa cum este scris în documentația Bitrix - construiți un catalog pe site conform regulilor oferite de Bitrix și construiți catalogul în 1C pe care Bitrix o cere. În mod ideal, creați totul de la zero și apoi poate totul va funcționa pentru dvs. din cutie.
    În al doilea rând, Bitrix nu este compatibil cu toate configurațiile 1C din cutie. Merită să verificați mai întâi
    În al treilea rând, nu există o lume ideală. De obicei, un client care dorește un site web are deja o afacere cu amănuntul, ceea ce înseamnă că are deja 1C, care este o groapă de gunoi uriașă. Și acest gunoi trebuie aruncat pe site. Și pentru ca site-ul să nu se dovedească a fi același gunoi, mecanismul de schimb trebuie îmbunătățit semnificativ.
    De foarte multe ori, cerințele clientului diferă foarte mult de viziunea produsului pe care echipa Bitrix și-a format-o, iar apoi rafinarea mecanismului de schimb poate fi destul de costisitoare, comparabilă ca intensitate a forței de muncă cu dezvoltarea unui modul de schimb unic pentru un caz specific.
    Prin urmare, nu este nevoie să vă faceți iluzii că veți putea să vă integrați cu ușurință site-ul cu 1C. Acestea sunt toate mașinațiunile marketerilor.

    Rafinarea schimbului cu 1C este, de asemenea, un subiect separat. Clasa CIBlockCMLImport este responsabilă pentru organizarea schimbului de catalog - 5.7k linii. Una dintre principalele metode care necesită cel mai adesea extensie este CIBlockCMLImport::ImportElement, care conține mai mult de 1k linii. Este suficient să-l moșteniți o dată, să actualizați produsul de câteva ori pe o perioadă lungă de timp și puteți obține un schimb nefuncțional cu 1C. Prin urmare, dezvoltatorii adesea nu se deranjează cu această clasă și încearcă să intre cumva în procesul de import folosind handlere de evenimente. Lucrul cu handlere de evenimente în Bitrix, în special în modulul infoblocks, nu este nicio experiență foarte plăcută, fie și doar pentru că evenimentele de același tip nu sunt aranjate uniform, iar unele evenimente pur și simplu nu sunt suficiente.
    În general, lucrurile sunt la fel de triste ca înainte.

    Incoerență

    Uneori mi se pare că dezvoltatorii diferitelor module nu comunică cu adevărat între ei. Când studiezi sursele nucleului, dai peste soluții foarte eterogene care ar putea fi implementate pe un singur motor, dar din anumite motive sunt implementate diferit.
    De exemplu, puteți lua proprietățile elementelor blocului de informații și ale UserFields. Ambele entități sunt de fapt un câmp suplimentar pentru o altă entitate. Are un tip, un sens și o descriere. Valoarea este stocată într-un tabel(e) separat(e) din baza de date; acestea au o interfață de acces la date aproximativ similară. Așadar, de ce să nu faci aceeași interfață pentru ei?
    La sfârșitul lunii martie, modulul de vânzare a fost actualizat la cea mai recentă versiune și au promis și proprietăți arbitrare pentru comenzi. Există într-adevăr o nouă interfață, a treia, pentru a lucra cu proprietăți extinse ale entității?

    Bitrix24

    Acesta este, în general, un subiect separat de discuție. Confuzia apare adesea din cauza acestui sistem. Există 2 opțiuni pentru B24 - SaaS și Standlone. Există o piață pentru B24, dar conține aplicații doar pentru versiunea SaaS! Dacă aveți o versiune în cutie, achiziționată cu 200 de mii, nu veți putea instala aplicații atât de populare precum designerul de documente și, în general, nu veți putea instala nicio aplicație de pe piață pentru Bitrix24 pe Bitrix24. Acesta este un paradox.
    În schimb, piața din versiunea obișnuită va fi disponibilă în Bitrix24. Există mult mai multe soluții acolo, dar ele sunt concentrate în principal în jurul Site Management, și nu B24.

    Bitrix24, după cum mi sa spus în departamentul de asistență tehnică, este un sistem holistic. Dacă interferați cu funcționarea componentelor standard ale sistemului, pregătiți-vă că această funcționalitate se va întrerupe cu actualizările ulterioare. Bitrix nu va conta pe tine pentru a finaliza componentele portalului și asta în ciuda faptului că își trimit oficial clienții către parteneri.

    Apropo, modificarea componentelor din versiunea în cutie a B24 este destul de o sarcină. Componente care generează cod js, care, folosind ajax, accesează codul php, care generează ca răspuns html+js. Acesta este un amestec infernal în care chiar nu vrei să te scufunzi.

    Documentație

    Documentația Bitrix rămâne în urmă cu 1-1,5 ani în urma dezvoltării produsului. Codul este foarte slab acoperit de phpDocs, iar de multe ori comentariul dinaintea clasei este doar pentru show, fiind generat automat în IDE.
    Însuși stilul de prezentare a documentației în sursele oficiale este adesea prea „liber”, iar conținutul unor articole din documentație poate să nu aibă nicio legătură cu Bitrix în sine.
    Cursul pentru dezvoltatori are o mulțime de informații, dar formatul în care dezvoltatorul este introdus în capacitățile sistemului nu oferă nivelul de percepție necesar. Dacă mergeți la Symfony Cookbook, totul este așezat acolo, toate aspectele necesare sunt descrise în funcție de versiune. În timp ce în Bitrix, cursul de formare pentru dezvoltatori conține, într-o manieră neclară, informații structurate despre nucleele vechi și noi, care sunt prezentate mai întâi separat și apoi amestecate, ceea ce le dă bătaie de cap începătorilor.

    Organizarea procesului de dezvoltare

    Datorită specificului sistemului, nu este atât de ușor să organizați un proces de dezvoltare convenabil. Nu cea mai recentă versiune a ediției Business (care era la îndemână) după instalare ocupă, gândiți-vă, aproape 530 de megaocteți

    $ du -s *|sort -nr|cut -f 2-|în timp ce citiți a;do du -hs $a;terminat 523M bitrix 204K încărcare 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 licență.php 4.0K install.config 4.0K index.php

    Din acest volum, o jumătate bună sunt binare și programe de instalare, care în general nu sunt necesare pentru controlul versiunilor. În general, se obișnuiește să nu se versioneze nucleul Bitrix. Dezvoltatorii Bitrix garantează înșiși integritatea nucleului și gestionează dependențele versiunilor diferitelor module în timpul actualizărilor. Dar acest lucru are imediat cel puțin un mare dezavantaj - este imposibil să desfășurați un proiect pe deplin funcțional cu o echipă de control al versiunilor; trebuie să îl asamblați în părți: obțineți sursele kernelului din backup-ul Bitrix și sursele dezvoltatorilor de la git .
    Lucrurile nu merg bine nici cu baza. Dacă tu însuți poți folosi migrarea în timpul dezvoltării, atunci Bitrix introduce actualizări în baza de date folosind scripturi obișnuite pe care nu le poți controla. Prin urmare, atunci când actualizați, va trebui în continuare să transferați copiile de siguranță ale bazei de date de la gazda centrală de dezvoltare la alți dezvoltatori.
    Oamenii buni, din nou, sunt instrumente de tăiere care ajută la organizarea tuturor acestor lucruri, dar, din păcate, încă nu este posibil să forțezi Bitrix să respecte aceste reguli.
    Oficial, Bitrix vă permite să aveți 2 copii ale unei distribuții. Unul este pentru producție, al doilea pentru dezvoltare. Dacă aveți mai mulți dezvoltatori într-un singur proiect, atunci sunteți, parcă, un haiduc). pot nitui câte copii ale dezvoltării doriți, pur și simplu nu se vor putea actualiza singuri.

    Colegi

    Și ultima întrebare pe care aș vrea să o abordez.
    Datorită faptului că Bitrix are o barieră scăzută la intrare, printre companiile care prestează servicii pe această piață există o mulțime de personal necalificat. Am văzut multe proiecte diferite de-a lungul carierei mele (mai mult de o sută în total) finalizate pe 1C-Bitrix. Pot spune cu încredere că 95% dintre ele au fost făcute într-o manieră „bugger”. Foarte rar am întâlnit proiecte a căror dezvoltare a avut un simț al abordării, dar acestea au fost doar câteva. Totul este foarte trist.

    concluzii

    Desigur, toate dezavantajele nu pot fi luate în considerare într-un singur articol. În fiecare zi întâlnești câteva lucruri mici care interferează cu munca ta în fiecare zi. Dar este pur și simplu imposibil să luați în considerare toate astfel de lucruri mici și, probabil, nu este nevoie.

    Ce concluzii se pot trage aici? Bitrix este un sistem extrem de complex datorită faptului că are o arhitectură prost concepută și multe defecte care continuă să trăiască în produs mult timp. Pe de altă parte, Bitrix este un sistem destul de simplu care necesită un nivel mult mai scăzut de calificări pentru a începe, spre deosebire de cadre.
    Sprijinirea acestui produs este o sarcină foarte ingrată în comparație cu produse precum Symfony, Laravel, Yii. Produsului îi place foarte mult să pună o spiță în roțile dezvoltatorilor atât neexperimentați, cât și cu experiență, ceea ce, la rândul său, se poate reflecta în costul serviciilor dezvoltate de dezvoltatorii Bitrix cu experiență.

    Regret că am petrecut atât de mult timp lucrând cu acest sistem? Mai degrabă da decât nu. Ar fi mai înțelept să petrec acest timp studiind ceva mai corect și mai logic (ceea ce încerc să fac în mod activ acum). Dar s-a întâmplat că nu era nimeni care să mă ghideze în direcția corectă la începutul călătoriei mele.

    Dacă sunteți un dezvoltator PHP începător, atunci veți prefera să studiați cadre precum Symfony, Laravel, Yii, ZendFramework decât Bitrix. Crede-mă, va plăti mai mult decât în ​​viitor. După ce stăpâniți oricare dintre aceste cadre, nu vă va fi dificil să dezvoltați ceva pentru Bitrix în viitor. Dacă nu aveți de ales, atunci studiați Bitrix, dar în timpul liber este mai bine să încercați să vă scufundați în lumea cadrelor pentru a vă pune creierul la loc.

    Dacă sunteți un dezvoltator cu experiență în Bitrix, dar fără experiență în alte cadre, atunci asigurați-vă că vă plonjați într-o altă lume; veți descoperi o mulțime de cunoștințe noi și utile care vă vor ajuta să scrieți soluții mult mai bune pentru 1C-Bitrix. Încercați să utilizați soluții din alte cadre în proiectele dvs., din fericire acest lucru nu este greu de realizat datorită abordării componente a acestuia din urmă și a compozitorului.

    Dacă sunteți client, atunci nu aveți încredere în marketerii Bitrix. Nimic nu va fi atât de ușor pe cât se spune în prezentările Bitrix. Și nu da vina pe dezvoltatorii tăi pentru asta, nu au nimic de-a face cu asta. Dacă doriți să creați un magazin online mare și complex la nivel de Eldorado/Mvideo/Sportmaster, atunci Bitrix poate să nu fie cea mai bună alegere.

    Adesea, în timpul dezvoltării, este necesar să selectați și să ștergeți o parte din datele marcate pentru ștergere. Această nevoie afectează în special bazele de date mari de informații cu zeci sau sute de mii de obiecte marcate pentru ștergere. Procesarea tuturor datelor simultan durează foarte mult timp, așa că, fără un instrument la îndemână, în care puteți vizualiza, selecta și șterge clar o parte din date, este imposibil.

    Această prelucrare este rezultatul muncii mele pe o anumită perioadă de timp. Funcționalitatea a fost extinsă la nevoie. Procesarea funcționează în modul obișnuit de aplicație și în clientul gros al unei aplicații gestionate. Implementat in procesare:

    • afișarea unui arbore de metadate cu capacitatea de a marca acele obiecte de bază de date care trebuie șterse;
    • afișarea numărului de obiecte marcate pentru ștergere, marcate de utilizator și posibile pentru ștergere;
    • capacitatea de a activa/dezactiva modul de operare exclusiv;
    • este posibilă ștergerea înregistrărilor aferente registrelor independente de informații;
    • posibilitatea de înlocuire în grup a link-urilor găsite;
    • afișarea legăturilor către obiectul care este șters sub forma unui arbore. Reflectarea în acest arbore a structurii legăturilor ciclice (recursive), evidențiind legăturile cheie (care împiedică ștergerea obiectului);
    • ștergerea unui pachet de legături ciclice într-o tranzacție - „ori totul sau nimic”, astfel încât „Obiect nu a fost găsit...” în baza de date;
    • posibilitatea de interogări de selecție arbitrare pentru fiecare obiect de metadate. În acest caz, cererile de selecție pot fi copiate. Acest lucru este util, de exemplu, atunci când trebuie să ștergeți documente pentru o anumită perioadă pentru o organizație; sau cărți de referință pe orice criteriu de selecție;
    • conservarea și restaurarea selecțiilor utilizate anterior;
    • indicator de progres la căutarea obiectelor marcate pentru ștergere și monitorizarea referențialității cu posibilitatea de a întrerupe operația.

    Pentru a oferi această funcționalitate, procesarea utilizează în mod activ RAM. Și, deși s-au luat măsuri pentru utilizarea optimă a memoriei RAM, în unele cazuri (baze de date mari, un număr mare de obiecte șterse) poate apărea o situație de lipsă de memorie. În astfel de cazuri, este necesar să limitați lista de obiecte care trebuie șterse sau să treceți la utilizarea clientului 1C pe 64 de biți.

    Cea mai recentă versiune 1.14. Schimbări:

    • Au fost reduse întârzierile în tranzițiile client-server pe cantități mari de date;
    • Logica îmbunătățită pentru configurațiile cu planuri de conturi fără tipuri de subconturi atribuite .

    Versiunea 1.13. Schimbări:

    • S-a remediat eroarea de flotare a procedurii formularului la încercarea de ștergere;
    • În forma obișnuită, a fost adăugată posibilitatea de a marca link-urile asociate pentru ștergere. Pentru a marca pentru ștergere, selectați unul sau mai multe rânduri de linkuri asociate și selectați „Marcați pentru ștergere” din meniul contextual. Blocul de legături aferent afișează un arbore. Când selectați un rând din arbore și apelați marcajul pentru ștergere, elementele tuturor rândurilor secundare sunt marcate pentru ștergere.
    • În forma obișnuită, a fost adăugată funcția „Afișare în listă” a arborelui de link-uri aferente.

    Versiunea de procesare 1.12. Schimbări:

    • S-a remediat o eroare necritică în forma obișnuită, care apare la actualizarea afișajului linkurilor aferente.

    Versiunea de procesare 1.11. Schimbări:

    • S-a remediat o eroare la colorarea cheilor asociate intrărilor din registrul de informații.

    Versiunea de procesare 1.10. Schimbări:

    • Mecanismul de acțiune al opțiunii „Șterge înregistrările registrelor de informații” a fost returnat. Această funcționalitate se aplică acum înregistrărilor registrelor de informații în care obiectul care este șters nu este master. Înregistrările cu un obiect șters sunt șterse automat;
    • Înregistrare/ștergere dezactivată cu indicatorul Schimb de date. Încărcare;
    • În arborele de linkuri, a fost adăugată afișarea atributului „Posted” și „Deletion Flag” pentru obiectele legate. În forma normală - sub forma unei imagini de linii, în forma gestionată - sub formă de casete de selectare.

    Versiunea de procesare 1.09. Schimbări:

    • A fost corectată o greșeală de tipar în algoritm la controlul referențialității directoarelor proprietare;
    • A fost schimbată o secțiune de cod pentru un formular gestionat care nu permitea lansarea acestuia pe una dintre configurațiile standard;
    • S-a adăugat control asupra părților tabelare standard ale obiectelor de configurare.

    Versiunea de procesare 1.08. Schimbări:

    • Lucrări de procesare implementate în clientul gros al unei aplicații gestionate.

    Versiunea de procesare 1.07. Schimbări:

    • A fost implementat un mecanism de monitorizare/înlocuire referințe pentru dimensiunile în afara bilanţului registrelor contabile.

    Versiunea de procesare 1.06. Schimbări:

    • Ștergerea arborelui de metadate după căutare a fost dezactivată - acum arborele de metadate este întotdeauna afișat;
    • S-a adăugat mecanism de înlocuire a legăturilor.
    • S-a adăugat afișarea numărului total de obiecte din baza de date;
    • Mecanismul de ștergere este comutat în modul Data Exchange Loading = True;
    • Opțiunea „Ștergeți datele din înregistrarea intrărilor” funcționează acum pentru dimensiunile principale și non-lide.

    Ajutorul de procesare a fost modificat:

    Prelucrarea constă din două părți: Arborele obiectelor șterse (3)Și Arborele de legături (4) la obiectul care este șters.

    În arborele obiectelor șterse se realizează setările de bază pentru căutarea legăturilor șterse. Arborele link-urilor le analizează pe cele legate de cele șterse

    obiect alte obiecte ale bazei de informaţii.Prelucrarea este controlată folosind Meniul acțiuni de bază (1), semne în copac

    obiectele de sters si Meniu funcții suplimentare (2)

    1. Meniul Acțiuni de bază

    -clar- inițializează arborele obiectelor de metadate, șterge rezultatele obținute anterior;

    -Găsi- caută obiecte de bază de informații marcate pentru ștergere folosind obiectele marcate. Căutarea se efectuează folosind directoare,

    Documente, planuri de conturi, planuri de tipuri de caracteristici, planuri de tipuri de calcul, planuri de schimb, sarcini, procese de afaceri;

    -Înlocuire- înlocuiește linkul șters cu cel indicat în coloana „Înlocuire”. La înlocuirea referințelor în detalii înregistrați dimensiunilepoate apărea o situație

    Când setul rezultat conține rânduri duplicate de-a lungul dimensiunilor, în astfel de situații rândurile duplicate sunt șterse. În acest caz, se acordă prioritate liniei cu cel mai mic număr în ordine.

    -Control- monitorizează natura de referință a obiectelor de bază de date găsite marcate pentru ștergere. Controlul se efectuează în funcție de detalii, măsurători,

    Resurse ale obiectelor de metadate, detalii ale părților tabelare ale obiectelor metadate, tipuri de calcul ale registrelor de calcul, conturi și analize ale registrelor contabile,

    Sarcini de conducere ale proceselor de afaceri,documente măsurătorile secvenței.

    -Șterge- șterge obiectele marcate care pot fi șterse.

    2. Meniul funcții suplimentare

    -Selecții- un submeniu cu operatii pentru selectarea unui arbore de obiecte (mai departe).

    -Ștergeți intrările din registrul de informații- activarea/dezactivarea modului de ștergere a înregistrărilor aferente registrelor independente de informații.

    -Afișează o listă de șterse- când această opțiune este activată, la ștergere, în mesajele de sistem va fi afișată o listă cu obiectele șterse;

    -Modul monopol- activarea/dezactivarea modului exclusiv de operare al bazei de date;

    -Găsiți și eliminați- se efectuează trei acțiuni la rând asupra obiectelor de metadate selectate: găsire - control - ștergere.

    3. Arborele obiectelor

    În starea sa inițială, reprezintă o listă completă de obiecte de metadate.Este necesar să setați mărci pentru acele obiecte

    printre care se va face o căutare pentru cele marcate pentru ștergere.

    Efectuând o căutare în coloana arborescentă " Total„Numărul de obiecte găsite marcate pentru ștergere va fi reflectat în coloană” Total linkuri" afișaj

    numărul total de legături ale acestui tip de obiect.

    Prin efectuarea controlului în coloana arborescentă " Posibil de șters" va afișa numărul de obiecte care pot fi șterse.

    Liniile arborelui sunt colorate în verde sau roșu în funcție de posibilitatea de ștergere a obiectelor găsite. Liniile care conțin informații sunt evidențiate cu albastru

    Folosind meniul contextual, arborele poate fi sortat în ordinea dorită.

    Funcțiile de setare/debifare a grupului operează pe liniile arborescente selectate.

    Caseta de bifat " marcă" este folosit atât la căutarea obiectelor marcate pentru ștergere, cât și la monitorizarea referențialității și înlocuirea legăturilor.

    Într-o coloană" Selecţie„Pentru grupuri de obiecte, puteți specifica o interogare personalizată pentru filtrarea suplimentară a obiectelor găsite pentru ștergere.Faceți dublu clic în coloana „Selectare”.

    deschide un formular de cerere personalizat

    O solicitare personalizată vă permite să aplicați un filtru suplimentar atunci când selectați obiecte marcate pentru ștergere.

    Selecția finală din cerere trebuie să conțină un câmp numit „ Legătură", acest câmp va fi un câmp de filtrare.

    În formularul de constructor de cereri (7) :

    -clar- șterge cererea curentă;

    -Mod implicit- generează o nouă cerere pe baza tipului de date al obiectului curent;

    -Obțineți parametri- actualizarea listei de parametri din cerere

    Interogările de filtrare pot fi instalate doar la nivelul celei de-a 2-a grupări a arborelui obiectelor șterse, adică. la nivelul secțiunii de metadate.

    4. Arborele linkurilor conexe

    Când activați o linie în arborele de obiecte, arborele de linkuri afișează structura ierarhică a linkurilor găsite către obiectul care este șters.

    Când o referință circulară a obiectelor este detectată într-o structură arborescentă, obiectul curent care este implicat în referința circulară este

    va fi redat în mod repetat și se va termina în arborele de referință ciclic.

    În rândurile acestui arbore sunt evidențiate cu roșu legăturile care nu pot fi șterse sau nu sunt șterse conform setărilor curente;

    care nu permit ștergerea în cele din urmă a obiectului curent.

    Făcând dublu clic pe rândurile acestui arbore, este posibil să vizualizați linkul asociat.

    5. Meniul contextual al arborelui de obiecte

    Meniul contextual al arborelui obiect funcționează conform liniilor selectate ale arborelui.

    Actiuni valabile:

    -Setați/debifați marcajele- setează/debifează liniile arborescente selectate, inclusiv liniile subordonate;

    -Sortare în ordine crescătoare/descrescătoare- gestionarea sortării rândurilor de copaci;

    -Selecție clară- (Comanda rapidă de la tastatură Ctrl-X ) interogările de filtru de date sunt șterse în rândurile de arbore selectate;

    1. Ce conținut informațional poate fi poziționat pe site-uri?

    Utilizatori
    + Curs de studiu
    + Blocuri de informații
    + Forme

    2. Când utilizați un sistem multi-site

    + bugetele comune ale utilizatorilor sunt folosite pentru toate site-urile
    - sunt create bugete de utilizator separate pentru fiecare site

    Ai nevoie de energie electrică bună? Mai întâi trebuie să înveți cum să-l controlezi, iar un dispozitiv de măsurare PKE poate face acest lucru perfect. Testerul de energie PKE-A-S4 permite inspecția energetică, măsurarea și înregistrarea cantităților de energie electrică, consumul de energie electrică, profilele de sarcină și măsurarea sarcinii circuitelor secundare.

    3. Dacă, la configurarea unui site, nu specificați numele site-ului în secțiunea „Parametri”, atunci

    + sistemul va folosi numele site-ului specificat în setările modulului principal
    - se va folosi valoarea specificată în parametrul „Nume” din secțiunea de setări principale
    - valoarea nu va fi definită

    4. Numărul de site-uri din sistem

    + determinat de contractul de licență și de licența pentru site-uri suplimentare
    - depinde de numărul de limbi de interfață din sistem
    - nelimitat

    5. Configurarea setărilor de limbă pentru secțiunea publică a site-ului se realizează:

    În formularul de editare a parametrilor directorului rădăcină al site-ului
    - sub forma creării/editării unei limbi
    + separat pentru fiecare site sub forma de creare și editare a unui site

    6. Pot configura mai mult de două site-uri într-o configurație multisite?

    + Puteți dacă cumpărați licențe suplimentare pentru site-uri suplimentare
    - Nu, acest lucru este interzis de contractul de licență
    - Da, fără restricții

    7. La activarea unei licențe pentru 4 site-uri suplimentare (în plus față de cele două disponibile pentru utilizare în mod implicit), poate fi utilizat următorul număr de copii ale nucleului de sistem:

    3
    - 6
    + 1
    - 2

    8. La vizualizarea structurii fișierelor, împărțirea pe site-uri se va efectua în următoarele cazuri:

    Organizare multi-site pe un singur domeniu
    + reprezentarea logică a structurii
    + organizarea de site-uri multiple pe diferite domenii

    9. Sunt utilizate tehnologii pentru transferul cookie-urilor utilizatorului între site-uri

    + dacă opțiunea „Distribuie cookie-uri către toate domeniile” este activată în setările modulului principal
    - întotdeauna dacă există un modul de statistică în sistem
    - dacă este activată opțiunea „Extindeți autorizarea la toate domeniile”.

    Setați opțiunea corespunzătoare în setările site-urilor
    + marcați opțiunea corespunzătoare în setările modulului principal
    - setați opțiunea corespunzătoare în setările Grupului de utilizatori

    11. Cum poți închide un singur site cu identificatorul ru pentru utilizatorii care vizitează?

    Folosind butonul: Setări -> Modul principal -> Proceduri de service -> Închidere acces pentru vizitatori
    + plasarea codului programului special în fișierul: /bitrix/php_interface/ru/init.php

    12. În setările căror module puteți separa parametrii pentru diferite site-uri?

    Niciunul nu permite
    - Modul Web Forms, Document Flow, Modul Structure Management
    - Magazin online, modul Blog, Modul principal
    - Toată lumea permite
    + Modul Blog, Modul Structure Management, Magazin online

    13. Pentru a crea site-uri suplimentare în sistem în afara celor permise în licență, trebuie să:

    Creați o limbă suplimentară de interfață
    - creați un cont în sistem, creați un subdirector separat
    + obțineți o licență pentru un site suplimentar, creați un cont în sistem, determinați calea către fișierele părții publice a site-ului

    14. Este posibil să separați drepturile de a vizualiza statisticile diferitelor site-uri din produs:

    Da, dacă efectuați setările de acces corespunzătoare în modulul Statistici
    + Nu, un utilizator care are dreptul de a vizualiza statistici va putea vedea statistici pentru toate site-urile

    15. Separat pentru fiecare site, puteți seta următoarele setări pentru modulul Structure Management:

    + setați tipurile de proprietate
    - organizarea unui sistem de acces la secțiuni pentru fiecare site
    - alegeți un editor HTML vizual și setările acestuia individual pentru fiecare site
    + setați tipurile de meniu
    + definiți numărul de opțiuni suplimentare de meniu

    Și ce spun ei, aceste voci. Una și. Același: sunt vânzătorul, sunt vânzătorul. Ce vei face. Ce poti face. Da, Ceapa de hidra în Rusia 2016. Și asta înseamnă că Bull Gates nu face contact. Nu merge. Sau poate că vine. Dar tu nu înțelegi. Poate Minotaurul este acel șobolan mort de pe tavan. . Probabil așa.

    Și nu ți-au explicat nimic. Și asta înseamnă o procedură specială. Așa că am plecat în întuneric. Bietele capre... Ei bine, nu contează, o să ne dăm seama și să-i pedepsim pe vinovați. Va fi pentru ei Instrucțiuni pentru lumânări nigella sativa, al treisprezecelea salariu în valută... Îl ridică. Am pus pe masă un caiet cu profilul lui Dante Alighieri pe copertă și am petrecut ceva timp concentrat pe hârtie cu un pix și am ghicit imediat că desenează aceleași profile ale lui Dante înăuntru, doar mici. Din anumite motive, acești oameni cred că în timpul lungului secol XX Instructiuni pentru lumanari nigella sativa nu le-au studiat metodele de lucru. Punând jos blocnotesul. A pășit spre mine, de parcă ar fi vrut să-mi vindece toate rănile emoționale cu îmbrățișarea lui tardivă, dar apoi a sunat telefonul de pe birou. Shmyga înjură și ridică telefonul. A ascultat câteva secunde, apoi chipul i-a devenit posomorât și atent. Da domnule, . A spus și a închis. Privind în sus la mine, și-a ridicat mâinile vinovat. Vezi ce se întâmplă.

    Secret comercial. Marinarii nu au întrebări”, a răspuns Malyuta. Stepa îl privi ciudat. Exact trei ore mai târziu a sunat căpitanul Lebedkin. De ce ai nevoie de viața mea? Salvat? - a întrebat el amenințător. - Ca să te implici în politică. Eu... - începu Styopa. — Nu te pișa, spuse vesel căpitanul. - Glumesc.

    Dori. Atunci merge mai departe, . spuse Chapaev ridicându-se din spate hotel de extaz. După ce am părăsit vagonul sediului, ne-am dus în spatele trenului. Ceea ce se întâmpla mi se părea din ce în ce mai ciudat. Câteva dintre trăsurile pe lângă care am trecut erau întunecate și păreau să fie. Gol. Nicăieri nu era lumină; Nu se auzea niciun sunet din spatele ușilor. Cu greu îmi venea să cred că în spatele panourilor de nuc, în suprafața lustruită a cărora se reflecta lumina trabucului lui Chapaev. Soldatul roșu doarme, dar am încercat să nu reflectez la asta.

    Până în 2003, specialiștii japonezi. A fost posibil să se dezvolte un set de mai multe microsonde care vape hidra direct creierul și a făcut posibilă, într-o oarecare măsură, obiectivarea imaginii percepției umane. Echipamentul japonez nu a putut determina exact ce simțea și gândea persoana observată. Dar a făcut posibilă obținerea unei imagini color (deși neclare) a ceea ce el vape hidra.Și nu numai în realitate, ci și în faza rapidă a somnului. Acest lucru a devenit posibil deoarece semnalul a fost preluat nu de la nervul optic, ci din acele zone. Creierele care sunt responsabile pentru reprezentarea directă. Echipamentul a fost achiziționat imediat de echipa lui Potashinsky. Semnalul de la un set de sonde implantate în creier ar putea fi transmis fără fir. Conexiuni care i-au permis bablonautului să ducă o viață normală, deloc constrânsă de participarea la experiment. Era necesar doar ca un receptor de semnal să fie amplasat undeva în apropiere. Care apoi transmitea informații către computer în timp real. Pe scurt, schema experimentelor lui Potashinsky arăta astfel: Mai întâi, un set de electrozi de control a fost implantat în creierul experimentatorului bablonaut (voluntari, ca de obicei, au fost selectați dintre tinerii ofițeri FSB pentru acest rol).

    Dintr-un punct mort. Ascultă, frate, spuse el, ce fel de natură este aceasta? Ce vrei sa spui? - a întrebat Isa. Ei bine, ai spus în mașină că corpul fragmentat de schije are aceeași natură ca și cel curcubeu. Și ce fel de natură este aceasta? Ar fi bine să nu întrebi despre asta. Frate, se încruntă Isa. De ce. Încă nu ești pregătit pentru asta. Cum nu gata. A marijuana amfetamină în același timp. Dacă aș fi gata, nu aș întreba. Deci poți răspunde. Sau.

    Curând. a întrebat. Cumpărați hașiș în Vladivostok acum, am spus, aici... Altcineva e pretențios. Un iubit ar putea fi jignit că nu are voie mai departe de holul rețelei. Dar Porfiry nu este așa. Primul lucru pe care l-am făcut a fost să mă conectez la ochelarii ei. Bine, spuse ea. Ce perciuni... Între timp, pun o poză cu ochelarii pe panou. Morphing-o cu vedere de la camera de tavan. Ifak ​​a ridicat orice transformare fără a-și încorda puterea. A fost monstruos. Acum Mara m-a văzut în ochelarii ei de ogment în locul aifakului și în același timp a putut să observe.

    Un cifr dezvoltat. Uneori observa că era din nou chinuit de vise recurente conform schemei 1. Sau conform schemei 2. Și deodată, în text deschis, ca un strigăt scăpat: Visat amestecuri de fumat non-stop, ucis de mine în copilărie... O voce în spatele paravanului. A tăcut. Ce face ea? - a întrebat Sam. „Am adormit”, a răspuns Natasha. Sam îi mângâie ușor vârful înțepător al abdomenului și se lăsă pe spate pe canapea. Natasha înghiți în liniște. Sam trase cutia de pe podea spre el, o deschise și scoase o cutie mică de sticlă. Borcanul, a scuipat roșu în el, l-a înșurubat și l-a aruncat înapoi - toată operațiunea l-a luat amestecuri de fumat non-stop secunde — Știi, Natasha, spuse el.

    După aceea el sfânt hașiș Hei, Tatarsky. Nici un raspuns. Tatarsky a mai așteptat un minut și și-a dat seama că a rămas singur. Singur cu mintea gata să devină sălbatică. Aveam nevoie urgent să mă ocup de ceva. Sună, șopti el. - La care. Gireev. El știe ce să facă. Pentru o lungă perioadă de timp. Nimeni nu a răspuns la telefon. În cele din urmă, pe al cincisprezecelea sau al douăzecilea ring, Gireev a răspuns sumbru: Bună. Andryusha.

    Nu, spuse el. Într-o cameră încuiată stă un bărbat care nu știe chineză. Îi dau notițe cu întrebări în chineză prin fereastră. Pentru el, acestea sunt doar bucăți de hârtie pe care sunt desenate mâzgălețe, al căror sens nu le înțelege. Dar camera lui este plină de cărți diferite. Reguli care descriu în detaliu cum și în ce secvență să răspunzi doar cu squiggles. Și el, acționând conform acestor reguli, dă răspunsuri în chineză într-o altă fereastră. Ei creează încredere deplină în toți cei care stau afară că știu chineză. Deși el însuși nu înțelege deloc despre ce îl întreabă. Adresă browser-ul hydra onion Care este sensul răspunsurilor lui? Introdus. Ei bine, l-am prezentat. Sura este aceeași cameră chinezească, doar automatizată. În loc de o persoană cu cărți de referință, există un scanner care citește hieroglife. O bază de date uriașă de referințe și reguli care vă permit să selectați hieroglife pentru răspuns.

    În mod ironic, asta mi-a adus claritate. Cel puțin în termeni practici. Mi-am dat seama de problema cu care mă confruntam. Nu este doar complex, este evaziv. A fost dificil chiar și să formulezi corect întrebările legate de acesta. Singura consolare părea Cum să găsiți site-ul Hydra în limba Torus, lucrurile sunt la fel de alunecoase. Cu conștiința umană. Nu am putut să mă descurc cu asta. Și am decis că cea mai bună cale de ieșire din situație ar fi să mă întorc. La afaceri ca de obicei, lăsând exercițiile existențiale pentru mai târziu sau uitând de ele.

    Curând drumul ducea la un sat bogat cu o biserică alb proaspăt pictată. Un soldat trist, cu un singur picior, într-o uniformă gri decolorată, stătea lângă gardul bisericii. Nu știi unde este Optina Pustyn. întrebă T. aplecându-se spre el de pe cal. Despre asta vorbesc băieții. întrebă soldatul. Care a fost recent înființat ca un stabiliment. Am hotărât că militarul a ieșit din minți. Cum a fost amenajat recent acest loc. Ceea ce înseamnă, în orice caz, totul este drept, cinste dumneavoastră, spuse soldatul și Ruleta Hydra mână, vei fi încă departe. Sunt doar două drumuri aici și ambele merg într-o singură direcție. Luați primul traseu sau al doilea. Dacă vrei un traseu mai scurt, atunci prin pădure. Există o furculiță acolo, așa că puteți lua orice parte.

    Și alte grimase, despre care cred că ați auzit multe... Lena nu a înțeles ce fel de tată-mamă proastă optsprezece era (tanarul a mormăit aceste cuvinte repede și în liniște), dar imediat a uitat de asta - ea a vrut deodată să ia o înghițitură de vin în valoare de douăzeci de mii de euro, încât i se lăsa gura apă. Un oftat liniștit a trecut prin hol, confirmând că cei adunați nu auziseră doar de grimase. Și am reușit să studiem în detaliu toate informațiile disponibile despre ei. „Recent, agențiile de informații occidentale au lansat o adevărată vânătoare pentru idioții noștri bogați”, a continuat tânărul. - Ai auzit, desigur, despre marile scandaluri adresa de hydra ceapa tk okey site hydra in torus arestări: mai întâi Courchevel, apoi Fiji, apoi boutique-ul Hermes, iar acum Saint Moritz, Maldive și Antarctica. Campania este atent planificată și are două obiective principale - în primul rând, discreditarea. Civilizația rusă - pentru a stabili controlul asupra resurselor sale prin colectarea de dovezi compromițătoare asupra proprietarilor principalelor sale active. Elita noastră a devenit o țintă, iar realitatea obiectivă a punctului actual din spațiu-timp este astfel încât. Am devenit ținte cu ea. Încruntat, a tăcut, parcă le-ar fi dat ascultătorilor săi ocazia să-și dea seama de gravitatea situației. Apoi zâmbetul trist i-a revenit pe față și a continuat: Trebuie să ținem situația sub control.

    Ea zâmbi. Cel puțin nu trebuie să pretindeți că sunteți ofensat de nevinovăție în fața propriilor oameni. Despre ce. Când l-am provocat. Când a sărit goală din Intrarea ceapă Hydrași stătea în fața lui cațeluș. Consideri asta o provocare. Cu siguranță. De ce, mă întreb, i-ai întors spatele? am ridicat din umeri. Pentru fiabilitate. Ce este mai ales de încredere în asta? Coada este mai aproape de țintă, am spus, nu prea încrezător. Bine. Și trebuie să te uiți peste umăr.

    Al treilea extaz de vanilie montan după cum urmează: Excelenței Sale O. Konstantin Petrovici Pobedonostsev, oficial. Prin prezenta, transmit Excelenței Voastre o traducere a unei inscripții antice egiptene. O foaie de foiță de aur găsită într-un medalion extaz de vanilie montan cadavrul părintelui Varsonofiy Netrebko în cadrul anchetei în cazul contelui T. Potrivit specialiștilor de la Muzeul Egiptean, conturul hieroglifelor ne permite datarea textului în epoca dinastiei a XVIII-a sau un timp ceva mai târziu. Pe inscripție scrie: Numele secret al hermafroditului cu cap de pisică, care dă putere asupra lui, este esența. ANGC. Dacă poți controla un hermafrodit cu acest nume. Amenda. Traducătorii care ANGC pot fi traduși și ca BHGV tradițional (sau altfel, în funcție de alegerea tabelelor de corespondență atunci când se utilizează registrele hieroglifice). Totuși, medalionul în sine nu poate fi transferat Excelenței Voastre, în ciuda solicitării dumneavoastră.

    Post navigare

  • Cele mai bune articole pe această temă