Come configurare smartphone e PC. Portale informativo
  • casa
  • Windows 7, XP
  • Rimozione di oggetti contrassegnati, sostituzione di collegamenti. Applicazione regolare e gestita

Rimozione di oggetti contrassegnati, sostituzione di collegamenti. Applicazione regolare e gestita

Ultimamente sono stati pubblicati molti articoli sugli svantaggi di Bitrix e sulle loro confutazioni. Visto che il bere è continuato così, aggiungo i miei 5 centesimi.
Nei commenti agli articoli hanno scritto che mancavano specifiche, esempi e una revisione più approfondita.

Questo articolo è un tentativo di scrivere questa recensione. Anche se no, questo è più un post di odio e dolore (forse anche un po' lamentoso). Questa è una versione estesa del post sugli svantaggi della pistola. Cercherò di descrivere la maggior parte delle cose che irritano me e i miei colleghi di Bitrix. Cercherò di raccogliere in un unico post tutti quegli inconvenienti che ogni giorno causano tanto dolore. Alla fine cercherò di trarre delle conclusioni.

Chi sono? Sì, in generale, uno sviluppatore normale. Lavoro con Bitrix da novembre 2010 (5,5 anni). Lavorando soltanto con Bitrix, non ho creato un singolo progetto commerciale utilizzando altri CMS, non ho utilizzato framework per creare siti web. Per linea di lavoro mi occupo principalmente di negozi online, della loro creazione, supporto e sviluppo.

TL;DR

Bitrix - UG, non dovresti entrare in questa piscina se non assolutamente necessario.

Invece di un'introduzione

Per iniziare, ti suggerisco di condurre un esperimento mentale. Proviamo a prendere due sviluppatori backend approssimativamente della stessa età e con approssimativamente la stessa esperienza lavorativa (diciamo 1 - 1,5 anni), solo che uno di loro ha lavorato per tutto questo tempo con 1C-Bitrix e l'altro con Symfony ( Per esempio). Puoi facilmente confrontare con quale insieme di tecnologie uno ha lavorato per tutto questo tempo, con cosa ha lavorato l'altro e quale insieme di conoscenze alla fine ha ricevuto durante questo periodo.

Nel caso di uno sviluppatore Symfony, questo sarà: php5/7 + conoscenza approfondita dell'OOP, modelli di progettazione generalmente accettati (almeno MVC, DI, Factory, Repository), capacità di sviluppare test unitari, utilizzare motori di template (almeno twig ), ORM (con Doctrine), compositore, git, standard PSR, esperienza di lavoro con la console e scrittura di applicazioni console, competenze di base nella configurazione di un server web.

Nel caso di uno sviluppatore 1C-Bitrix, questo sarà php5, html/css + javascript/jquery (non ci sono motori di template pronti all'uso e Bitrix inserisce la logica nei template, qualunque cosa si possa dire, dovrai farlo armeggiarci), forse git (e questo dipende molto dall'azienda, alcuni dinosauri stanno ancora segando in produzione tramite FTP), se sei fortunato - un po' di sql e... tutto qui?

Capisco che tutto questo è molto individuale e che l'ambiente di una persona può svolgere un ruolo molto importante. Ma qui sto parlando di ciò che lo sviluppatore del sistema è spinto a fare fuori dagli schemi. Nella maggior parte dei casi, uno sviluppatore Bitrix ha competenze molto inferiori rispetto agli sviluppatori di altri framework/CMS - e questo è un fatto indiscutibile. E tutto perché Bitrix inizialmente dà troppa libertà in assenza di un'architettura chiara, documentazione e soluzioni corrette, mentre Symfony offre tutto ciò di cui hai bisogno.

Solo una volta è venuta nella nostra azienda una persona esperta non proveniente dal mondo 1C-Bitrix (della regione) e si è rivelata superiore ai suoi colleghi con la stessa esperienza, semplicemente per il fatto che era stato precedentemente inserito nel giusta traccia.
Anche io sono così. Sfortunatamente, fin dall'inizio sono stato ingannato dalle stesse sciocchezze del marketing e mi sono ritrovato in un ambiente non molto buono. Io stesso capisco e sento che i miei colleghi con esperienze lavorative simili, ma nello stesso Symfony, hanno una visione più ampia, e questo è un effetto collaterale molto forte di Bitrix.
Tutto ciò suggerisce che se vuoi svilupparti nel mondo dello sviluppo web, non dovresti assolutamente scegliere Bitrix come base.

Confrontando i due sviluppatori, voglio attirare l'attenzione sul quadro in cui ci costringe il sistema e sulla libertà che offre. Sia Bitrix che Symfony: entrambi forniscono una flessibilità quasi illimitata e, in linea di principio, su ciascuno di essi è possibile creare un prodotto di qualsiasi complessità. Tuttavia, il sistema dovrebbe aiutare lo sviluppatore a risolvere i problemi, invece di mettere i bastoni tra le ruote. E qui Bitrix perde moltissimo.

Marketing

Voglio spendere subito qualche parola su questo, perché... questa è la componente principale del successo di Bitrix.
Possiamo dire che l'intero Bitrix, anche la documentazione per gli sviluppatori, è intriso di spirito di marketing. Anche lì scrivono che il loro prodotto è “così bello che è apprezzato e rispettato da tutti i nostri partner” (prova, blocco “Struttura”). Bitrix impiega buoni esperti di marketing che sanno con competenza come presentare il proprio prodotto. Una volta ogni sei mesi organizzano conferenze per i partner, dove parlano di cosa è stato fatto e quali sono i loro piani. Come dimostra la pratica, questi piani non si realizzano mai in tempo e molto spesso i rilasci sono incompleti o pieni di errori.
Ad esempio, il clamoroso refactoring del modulo di vendita, il cui rilascio è stato ritardato di oltre un anno, e anche l'ultima data di rilascio (23 dicembre 2015) è mancata di 3 mesi, e un nuovo negozio e BUS (Bitrix ed. “Gestione Siti”) versione 16 è stata rilasciata solo a fine marzo 2016. Di conseguenza, dopo l'aggiornamento, gli utenti non solo non hanno ricevuto nuove funzionalità. Gli utenti hanno ricevuto un negozio per lo più inutilizzabile e una pila di nuovo codice non documentato da avviare.
Ai nuovi strumenti vengono dati nomi così altisonanti che tutti conoscono: Sito composito - accelerazione x100; Blocchi ad alto carico; Bitrix BigData. In effetti, queste parole nascondono cose abbastanza ordinarie che non sono all'altezza del loro nome.
E questo approccio è riscontrabile ovunque, purtroppo. Dall'esterno, il prodotto sembra una caramella che hai acquistato, installato e utilizzato. Ma se ti allontani dalla consegna standard con Bitrix, tutto qui, il mantenimento della funzionalità durante gli aggiornamenti si trasforma in un inferno.
Tuttavia, per prima cosa, l’argomento marketing molto probabilmente verrà affrontato in questo post più di una volta.

Architettura

Per dieci anni Bitrix si è disperatamente spinta in un vicolo cieco. Ogni nuova funzionalità del prodotto è stata rilasciata in conformità con gli interessi dell'azienda, senza un adeguato sviluppo dal punto di vista tecnico. E, naturalmente, tutto questo è cresciuto come una palla di neve.
Se ci pensi, Bitrix non ha un'architettura in quanto tale. Non esistono nemmeno regole generalmente accettate e formulate che consentano di seguire questa architettura. Nel corso per sviluppatori, nella sezione Architettura del prodotto, si dice che Bitrix segue l'architettura MVC e fornisce uno schema:

Voglio dire subito che questo MVC è molto diverso dalla versione classica. C'è una sostituzione molto forte di concetti qui, in realtà non c'è MVC qui, c'è semplicemente una sorta di divisione astratta in moduli, componenti e modelli di componenti. E l'intero sito è costruito con questi mattoni. Ma ciascuno di questi mattoni può assumere compiti diversi e quindi sono strettamente interconnessi.
Cercherò di esaminare ciascuno di questi aspetti dell'architettura in modo più dettagliato.

M: modello o API

È difficile per me giudicare l'API del sistema come modello. Sì, l'API fornisce un'interfaccia per accedere ai dati e consentirti di manipolarli. Ma l'API Bitrix ti consente di lavorare non solo con i dati, ma anche con i modelli e anche con le richieste degli utenti. Oh beh... questa è solo la mia opinione.
Al momento, Bitrix ha 2 opzioni API. Convenzionalmente, possono essere suddivisi in vecchio E nuovo. La nuova API si chiama D7 (onestamente, non ricordo perché, ma Rizhikov ne ha parlato in una delle conferenze dei partner).

La vecchia API è una raccolta di antipattern, terribili esempi di codice errato. In Bitrix è sempre stato considerato normale chiamare staticamente metodi non statici e viceversa richiedere state quando non è appropriato. Ad esempio, il noto CIBlockElement::GetList è forse uno dei metodi utilizzati più frequentemente nello sviluppo. La sua implementazione contiene più di 500 righe di codice, utilizza valori globali, crea query terrificanti e colossali e contiene codice non realistico, semplicemente illeggibile e non documentato.

Guardiamo

Function GetList($arOrder=array("SORT"=>"ASC"), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array()) ( /* Combinazioni di filtri: CHECK_PERMISSIONS= "N" - controlla i permessi dell'utente corrente per l'infoblock MIN_PERMISSION="R" - quando vengono controllati i permessi, il livello di accesso minimo SHOW_HISTORY="N" - aggiungi elementi della cronologia all'elenco SHOW_NEW="N" - in caso contrario aggiungi elementi della cronologia, quindi aggiungi elementi nuovi, ma non pubblicati */ 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(ORA_SECONDI DA 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(ORA_SECONDI DA BE.ACTIVE_TO)>0, ".$DB->DateToCharFunction("BE.ACTIVE_TO", "FULL") .", ".$DB->DateToCharFunction("BE.ACTIVE_TO", "SHORT").")"), "SORT"=>"BE.SORT", "NAME"=>"BE.NAME", " PREVIEW_PICTURE"=>"BE.PREVIEW_PICTURE", "PREVIEW_TEXT"=>"BE.PREVIEW_TEXT", "PREVIEW_TEXT_TYPE"=>"BE.PREVIEW_TEXT_TYPE", "DETAIL_PICTURE"=>"BE. DETAIL_PICTURE", "DETAIL_TEXT"=>"BE.DETAIL_TEXT", "DETAIL_TEXT_TYPE"=>"BE.DETAIL_TEXT_TYPE", "SEARCHABLE_CONTENT"=>"BE.SEARCHABLE_CONTENT", "WF_STATUS_ID"=>"BE.WF_STATUS_ID", "WF_PARENT_ELEMENT_ID" =>"BE.WF_PARENT_ELEMENT_ID", "WF_LAST_HISTORY_ID"=>"BE.WF_LAST_HISTORY_ID", "WF_NEW"=>"BE.WF_NEW", "LOCK_STATUS"=>"se (BE.WF_DATE_LOCK è nullo, "verde", se( DATA_ADD(BE.WF_DATE_LOCK, intervallo ".$MAX_LOCK." MINUTO) "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. NOME ","")," ",ifnull(UL.COGNOME,""))", "CREATED_USER_NAME"=>"concat("(",UC.LOGIN,"") ",ifnull(UC.NAME," "), " ",ifnull(UC.COGNOME,""))", "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.MM.GG", "BE.DATE_CREATE"), "BP_PUBLISHED"=>"se (BE.WF_STATUS_ID = 1, "Y", "N")",); unset($shortFormatActiveDates); unset($formatActiveDates); $bDistinto = falso; CIBlockElement::PrepareGetList($arIblockElementFields, $arJoinProps, $bOnlyCount, $bDistinct, $arSelectFields, $sSelect, $arAddSelectFields, $arFilter, $sWhere, $sSectionWhere, $arAddWhereFields, $arGroupBy, $sGroupBy, $arOrder, $arSqlOrder, $arAddOrderByFields, $arIBlockFilter, $arIBlockMultProps, $arIBlockConvProps, $arIBlockAllProps, $arIBlockNumProps, $arIBlockLongProps); $arFilterIBlocks = isset($arFilter["IBLOCK_ID"])? array($arFilter["ID_IBLOCK"]): array(); //****************DALLA PARTE**************************** ** ************** $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"] as $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 = true; if($db_prop["VERSIONE"]==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 ." SU 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." SU 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"] as $propID => $db_prop) ( $i = $db_prop["CNT"]; if($db_prop["VERSIONE"] == 2 && $db_prop["MULTIPLO"] == "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"; ) se ($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 IS NULL)".($arFilter["SHOW_NEW"]=="Y"? " OR BE.WF_NEW="Y"": "").")": "")."n"; if ( $db_prop["bJoinIBlock"]) $sFrom .= "tttLEFT JOIN b_iblock B".$i." SU B".$i.".ID = BE".$i.".IBLOCK_IDn"; if($db_prop [ "bJoinSection"]) $sDa . = "tttLEFT JOIN b_iblock_section BS".$i." SU 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"] as $propID => $db_prop) ( $i = $db_prop["CNT"]; list($propID, $link) = esplode("~ ", $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 AND ". (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) = esplode("~", $propID, 2) ; if($db_prop["MULTIPLE"]=="Y") $bDistinct = true; if($db_prop["VERSION"]==2) $strTable = "b_iblock_element_prop_m".$db_prop["IBLOCK_ID"] ; else $strTable = "b_iblock_element_property"; if($db_prop["bFullJoin"]) $sFrom .= "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 "] as $propID => $db_prop) ( $i = $db_prop["CNT"]; list($propID, $link) = esplode("~", $propID, 2); if($db_prop[ "VERSIONE "] == 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"; altrimenti $sDa . = "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"; ) se ($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"]? "INTERNO": "SINISTRA")." JOIN b_rating_vote RVV ON RVV.ENTITY_TYPE_ID = "IBLOCK_ELEMENT" E RVV.ENTITY_ID = BE.IDn"; //********************FINE DELLA PARTE**************************** ********* ****************** $bCatalogSort = false; if(count($arAddSelectFields)>0 || count($arAddWhereFields)>0 || count($arAddOrderByFields)>0) ( if(CModule::IncludeModule("catalog")) ( $res_catalog = CCatalogProduct::GetQueryBuildArrays( $arAddOrderByFields, $arAddWhereFields, $arAddSelectFields); if($sGroupBy=="" && !$bOnlyCount && !(is_object($this) && isset($this->strField))) $sSelect .= $res_catalog["SELECT "]." "; $sFrom .= str_replace("LEFT JOIN", "ntttLEFT JOIN", $res_catalog["FROM"])."n"; //$sWhere .= $res_catalog["DOVE"]." "; spostato in MkFilter if(is_array($res_catalog["ORDER"]) && count($res_catalog["ORDER"])) ( $bCatalogSort = true; foreach($res_catalog["ORDER"] as $i=>$ val) $arSqlOrder[$i] = $val; ) ) ) $i = array_search("CREATED_BY_FORMATTED", $arSelectFields); if ($i !== false) ( if ($sSelect && $sGroupBy=="" && !$bOnlyCount && !(is_object($this) && isset($this->strField))) ( $sSelect .= " ,UC. NOME UC_NOME, UC.COGNOME UC_COGNOME, UC.SECOND_NAME UC_SECOND_NAME, UC.EMAIL UC_EMAIL, UC.ID UC_ID, UC.LOGIN UC_LOGIN"; ) else ( unset($arSelectFields[$i]); ) ) $sOrderBy = ""; foreach($arSqlOrdine as $i=>$val) ( if(strlen($val)) ( if($sOrderBy=="") $sOrderBy = " ORDINA PER "; 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; return "SELECT ".$sSelect.$strSql; ) if($bOnlyCount) ( $res = $DB->Query(" SELECT ".$sSelect.$strSql, false, "FILE: ".__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->Query(" SELECT ".$sSelect." ,@rank_r:=@rank_r+1 AS rango1 ,if (BE.ID = ".$nElementID.", @rank_e:=@rank_r, null) rango2 ".$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 tra @rank_e-$nPageSize e @rank_e+$nPageSize "); ) else ( $DB->Query("SET @rank=0"); $res = $DB->Query(" SELECT * FROM (SELECT ".$sSelect." ,@rank:=@rank+1 AS RANK ".$strSql.$sOrderBy." LIMIT 18446744073709551615) el0 WHERE el0.ID = ".$nElementID." "); ) ) else ( if($sGroupBy == "") ( $res_cnt = $DB->Query( "SELECT COUNT(".($bDistinct? "DISTINCT BE.ID": ""x"").") as C ".$strSql); $res_cnt = $res_cnt->Fetch(); $cnt = $res_cnt ["C"]; ) else ( $res_cnt = $DB->Query("SELECT "x" ".$strSql); $cnt = $res_cnt->SelectedRowsCount(); ) $strSql = "SELECT ".$sSelect .$strSql.$sOrderBy; $res = nuovo CDBResult(); $res->NavQuery($strSql, $cnt, $arNavStartParams); ) ) else//if(is_array($arNavStartParams)) ( $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy; $res = $DB->Query($strSql, false, "FILE: ".__FILE__."
LINE: ".__LINE__); ) $res = nuovo CIBlockResult($res); $res->SetIBlockTag($arFilterIBlocks); $res->arIBlockMultProps = $arIBlockMultProps; $res->arIBlockConvProps = $arIBlockConvProps; $res-> arIBlockAllProps = $arIBlockAllProps; $res->arIBlockNumProps = $arIBlockNumProps; $res->arIBlockLongProps = $arIBlockLongProps; return $res; )

Come puoi immaginare, questo metodo riceve un elenco di elementi del blocco di informazioni dal database e per ottenere l'elenco non è necessaria la creazione di un'istanza della classe CIBlockElement. Tuttavia, per aggiungere un elemento del blocco informazioni, è necessario uno stato e solo per registrare informazioni sull'ultimo errore verificatosi in una proprietà pubblica della classe.

La vecchia API fa un uso massiccio di variabili globali come $APPLICATION, $USER, $DB. Sono istanze di determinate classi e nella documentazione venivano orgogliosamente chiamate singleton, anche se ora non ho più trovato queste parole.
Per generare un errore, ad esempio, nei gestori di eventi, è necessario utilizzare il metodo $APPLICATION->ThrowException(), che in realtà non genera eccezioni.

Funzione pubblica ThrowException($msg, $id = false) ( $this->ResetException(); if(is_object($msg) && (is_subclass_of($msg, "CApplicationException") || (strtolower(get_class($msg)) =="capplicationexception"))) $this->LAST_ERROR = $msg; else $this->LAST_ERROR = new CApplicationException($msg, $id); )

E sì, tutta questa bellezza viene ancora utilizzata nello sviluppo di nuovi progetti, perché... D7 non supporta ancora tutte le funzionalità della vecchia API. Lo stesso modulo infoblock consente comunque di eseguire solo una selezione di entità, e non interamente. Non è ancora possibile creare un nuovo elemento o aggiornarne uno esistente utilizzando la nuova API.

La nuova API è già leggermente diversa da quella vecchia. Innanzitutto, tutto il codice del nuovo kernel è diviso in namespace, dove esiste una chiara dipendenza dal modulo. Ad esempio, l'analogo di CIBlockElement::GetList del nuovo core è BitrixIblockElementTable::getList, dove lo spazio dei nomi root è il nome del fornitore e lo spazio dei nomi successivo è il nome del modulo. Affinché ciò funzioni, Bitrix ha scritto il proprio caricatore automatico, BitrixMainLoader::autoLoad, che non è affatto compatibile con PSR-0/4.

In realtà, il codice del caricatore automatico sotto forma di una funzione

Funzione statica pubblica autoLoad($className) ( $file = ltrim($className, "\"); // corregge l'ambiente web $file = strtr($file, static::ALPHA_UPPER, static::ALPHA_LOWER); static $documentRoot = null; if ($documentRoot === null) $documentRoot = static::getDocumentRoot(); if (isset(self::$arAutoLoadClasses[$file])) ( $pathInfo = self::$arAutoLoadClasses[$file]; if ($percorsoInfo["modulo"] != "") ( $m = $percorsoInfo["modulo"]; $h = isset(self::$arLoadedModulesHolders[$m]) ? self::$arLoadedModulesHolders[$m ] : "bitrix"; include_once($documentRoot."/".$h."/modules/".$m."/" .$pathInfo["file"]); ) else ( require_once($documentRoot.$pathInfo ["file"]); ) return; ) if (preg_match("#[^\\/a-zA-Z0-9_]#", $file)) return; if (substr($file, -5) = = "tabella") $file = substr($file, 0, -5); $file = str_replace("\", "/", $file); $arFile = esplode("/", $file); if ($arFile === "bitrix") ( array_shift($arFile); if (vuoto($arFile)) return; $module = array_shift($arFile); if ($module == null || vuoto($arFile) ) ritorno; ) else ( $modulo1 = array_shift($arFile); $module2 = array_shift($arFile); if ($module1 == null || $module2 == null || vuoto($arFile)) return; $module = $module1 .".".$module2; ) if (!isset(self::$arLoadedModulesHolders[$module])) return; $filePath = $documentRoot."/".self::$arLoadedModulesHolders[$module].."/modules/".$module."/lib/".implode("/", $arFile).".php" ; if (file_exists($filePath)) require_once($filePath); )

La nuova API mostra molto amore per Singleton:

  • BitrixMainApplication::getInstance - istanza dell'applicazione
  • BitrixMainConfigConfiguration::getInstance - istanza di classe per la gestione delle configurazioni
  • BitrixMainPageAsset::getInstance - Istanza del gestore risorse
  • BitrixMainEventManager::getInstance - gestore eventi

Forse in futuro tutto questo acquisirà un proprio ServiceLayer (nel nuovo core c'è un certo BitrixMainServiceManager, che non è ancora utilizzato e non è documentato), ma ci sono ancora poche speranze.

ORM è un'altra delle innovazioni del D7, e questo è qualcosa che può affermare di essere un vero modello! Puoi distinguere una classe di entità ORM da qualsiasi altra classe tramite il suo nome. Una classe di entità deve sempre terminare con Table (ElementTable, SezioneTable, OrderTable, ecc.). Inoltre, paradossalmente, il nome del file con la classe entità ORM non dovrebbe terminare in Table. Ad esempio, per ElementTable dobbiamo creare un file element.php. Lo screenshot seguente mostra il contenuto della directory lib (il caricamento automatico di D7 funziona solo in questa directory) del modulo iblock. Prova a determinare a occhio quali sono le entità ORM e quali sono le classi ordinarie con logica aziendale.


ORM, nel complesso, non è ancora niente di speciale. Ti consente di descrivere le tabelle del database sotto forma di classi e ti consente di eseguire query su queste tabelle e collegarle tra loro. Non esiste ActiveRecord o Repository e non è previsto.

Un esempio di una tipica classe di entità ORM per un elemento infoblock

*

  • ID int obbligatorio *
  • TIMESTAMP_X data/ora opzionale *
  • MODIFIED_BY int facoltativo *
  • DATE_CREATE data/ora opzionale *
  • CREATED_BY int facoltativo *
  • IBLOCK_ID int obbligatorio *
  • IBLOCK_SECTION_ID int facoltativo *
  • ACTIVE bool opzionale default "Y" *
  • ACTIVE_FROM data/ora opzionale *
  • ACTIVE_TO dataora facoltativo *
  • SORT int opzionale predefinito 500 *
  • NOME stringa(255) obbligatorio *
  • PREVIEW_PICTURE int facoltativo *
  • PREVIEW_TEXT stringa opzionale *
  • PREVIEW_TEXT_TYPE enum ("testo", "html") opzionale predefinito "testo" *
  • DETAIL_PICTURE int facoltativo *
  • DETAIL_TEXT stringa opzionale *
  • DETAIL_TEXT_TYPE enum ("testo", "html") opzionale predefinito "testo" *
  • SEARCHABLE_CONTENT stringa opzionale *
  • WF_STATUS_ID int opzionale predefinito 1 *
  • WF_PARENT_ELEMENT_ID int facoltativo *
  • Enumerazione WF_NEW ("N", "Y") opzionale *
  • WF_LOCKED_BY int facoltativo *
  • WF_DATE_LOCK data/ora opzionale *
  • WF_COMMENT stringa facoltativa *
  • IN_SECTIONS bool opzionale predefinito "N" *
  • XML_ID stringa(255) opzionale *
  • CODICE stringa(255) opzionale *
  • TAG stringa(255) opzionale *
  • TMP_ID stringa(40) opzionale *
  • WF_LAST_HISTORY_ID int facoltativo *
  • SHOW_COUNTER int facoltativo *
  • SHOW_COUNTER_START data e ora facoltativo *
  • PREVIEW_PICTURE_FILE riferimento a (@link BitrixFileFileTable) *
  • DETAIL_PICTURE_FILE riferimento a (@link BitrixFileFileTable) *
  • Riferimento IBLOCK a (@link BitrixIblockIblockTable) *
  • Riferimento WF_PARENT_ELEMENT a (@link BitrixIblockIblockElementTable) *
  • Riferimento IBLOCK_SECTION a (@link BitrixIblockIblockSectionTable) *
  • MODIFIED_BY_USER riferimento a (@link BitrixUserUserTable) *
  • CREATED_BY_USER riferimento a (@link BitrixUserUserTable) *
  • Riferimento WF_LOCKED_BY_USER a (@link BitrixUserUserTable) * * * @package BitrixIblock **/ class ElementTable extends MainEntityDataManager ( const TYPE_TEXT = "testo"; const TYPE_HTML = "html"; /** * Restituisce il nome della tabella DB per l'entità. * * @return string */ public static function getTableName( ) ( return "b_iblock_element"; ) /** * Restituisce la definizione della mappa entità. * * @return array */ public static function getMap() ( return array("ID" => new MainEntityIntegerField("ID", array("primary " => true, "completamento automatico" => true, "titolo" => 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_ID_FIELD"),)), " ACTIVE" => new MainEntityBooleanField("ACTIVE", array("values" => array("N", "Y"), "default_value" => "Y", "titolo" => 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("titolo" => Loc::getMessage("ELEMENT_ENTITY_ACTIVE_TO_FIELD"),)), "SORT" => new MainEntityIntegerField("SORT", array("default_value" => 500, "titolo" => Loc::getMessage ("ELEMENT_ENTITY_SORT_FIELD"),)), "NAME" => new MainEntityStringField("NAME", array("required" => true, "validation" => array(__CLASS__, "validateName"), "title" => Loc ::getMessage("ELEMENT_ENTITY_NAME_FIELD"),)), "PREVIEW_PICTURE" => nuovo MainEntityIntegerField("PREVIEW_PICTURE", array("title" => Loc::getMessage("ELEMENT_ENTITY_PREVIEW_PICTURE_FIELD"),)), "PREVIEW_TEXT" => nuovo MainEntityTextField("PREVIEW_TEXT", array("title" => Loc::getMessage("ELEMENT_ENTITY_PREVIEW_TEXT_FIELD"),)), "PREVIEW_TEXT_TYPE" => new MainEntityEnumField("PREVIEW_TEXT_TYPE", array("values" => array(self:: TYPE_TEXT, self::TYPE_HTML), "default_value" => self::TYPE_TEXT, "title" => Loc::getMessage("ELEMENT_ENTITY_PREVIEW_TEXT_TYPE_FIELD"),)), "DETAIL_PICTURE" => new MainEntityIntegerField("DETAIL_PICTURE", array( "title" => Loc::getMessage("ELEMENT_ENTITY_DETAIL_PICTURE_FIELD"),)), "DETAIL_TEXT" => new 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_STATUS_ID" => new MainEntityIntegerField( "Wf_status_id", Array ("Titolo" => Loc :: getmessage ("Element_entity_wf_status_id_field"),)), "WF_PARENT_ELEMENT_ID" => Nuovo MainentityintegerField , Array ("Titolo" => Loc :: Getmessage ("Element_entity_wf_part_element_id_field"),) ), "WF_NEW" => new MainEntityEnumField("WF_NEW", array("values" => array("N", "Y"), "title" => Loc::getMessage("ELEMENT_ENTITY_WF_NEW_FIELD") ,)), "WF_LOCKED_BY" => new MainEntityIntegerField("WF_LOCKED_BY", array("title" => Loc::getMessage("ELEMENT_ENTITY_WF_LOCKED_BY_FIELD"),)), "WF_DATE_LOCK" => new MainEntityDatetimeField("WF_DATE_LOCK", array( "title" = > Loc::getMessage("ELEMENT_ENTITY_WF_DATE_LOCK_FIELD"),)), "WF_COMments" => nuovo 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 " => new MainEntityStringField("XML_ID", array("validation" => array(__CLASS__, "validateXmlId"), "title" => Loc::getMessage("ELEMENT_ENTITY_XML_ID_FIELD"),)), "CODE" => new 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

    E un esempio di lavoro con questa entità

    //Selezione dei dati $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["NOME"]) - " . $arElement["DATA_ACTIVE_FROM"]->format("d.m.Y H:i:s"); ) //Aggiunta di un record $addResult = BitrixIblockElementTable::add([ "NAME" => "Nome del nuovo elemento", "IBLOCK_ID" => CATALOG_IBLOCK_ID ]); if (!$addResult->isSuccess()) ( echo implode("
    " ,$addResult->getErrorMessages()); )

    Bitrix è molto orgogliosa del suo modulo Highload Blocks, scritto interamente utilizzando D7.
    In precedenza avevano solo blocchi di informazioni come spazio di archiviazione per un insieme arbitrario di informazioni. Un blocco informazioni, per chi non lo sapesse, è un'entità che viene memorizzata nel database come complesso di più tabelle (1 tabella per i campi “base” di un elemento del blocco informazioni e fino a 2 tabelle per le proprietà di un elemento elemento del blocco informazioni). Tutti i campi degli elementi di base tutti gli infoblock sono memorizzati in una tabella. Se disponi di 15 blocchi di informazioni, ciascuno dei quali contiene 500.000 elementi, tutti questi elementi saranno effettivamente in un'unica tabella. Ulteriori proprietà degli elementi infoblock vengono unite da altre tabelle. Se si tratta di blocchi di informazioni della prima versione, anche tutte le proprietà di tutti i blocchi di informazioni sono in un'unica tabella e, nel caso dei blocchi di informazioni 2.0 (ciao, marketing), le proprietà di ciascun blocco di informazioni sono già divise in tabelle diverse .
    E tutta questa faccenda naturalmente ha subito un notevole rallentamento già su set di dati relativamente piccoli. 400.000 elementi in un blocco di informazioni stanno già rallentando notevolmente il lavoro del pannello di amministrazione. Gli esperti di marketing di Bitrix hanno pensato e creato i blocchi Highload! La differenza nell'implementazione tra i blocchi di informazioni convenzionali è minima. Ora, per ogni blocco ad alto carico, viene creata la propria tabella + inoltre, viene creata un'altra tabella per memorizzare più valori. Hanno definito un nome orgoglioso il solito approccio alla creazione di una tabella regolare in un database carico elevato semplicemente perché rallenta meno dei normali blocchi di informazioni!
    Inoltre, all'interno del modulo, affinché funzioni secondo D7, le classi di entità vengono generate dinamicamente e valutate ad ogni colpo: questo è l'highload.

    Guarda questo

    Funzione statica pubblica compileEntity($hlblock) ( globale $USER_FIELD_MANAGER; // genera entità e gestore dati $fieldsMap = array(); // aggiungi ID $fieldsMap["ID"] = array("data_type" => "intero", "primary" => true, "autocomplete" => true); // crea la classe del datamanager $entity_name = $hlblock["NAME"]; $entity_data_class = $hlblock["NAME"]; if (!preg_match("/^ +$/i", $entity_data_class)) ( lancia new MainSystemException(sprintf("Nome entità non valido `%s`.", $entity_data_class)); ) $entity_data_class .= "Tabella"; if (class_exists($entity_data_class)) ( // ricostruisce se esiste già EntityBase::destroy($entity_data_class); ) else ( $entity_table_name = $hlblock["TABLE_NAME"]; // crea con una mappa vuota $eval = " class ".$entity_data_class. " estende ".__NAMESPACE__."DataManager ( funzione statica pubblica getTableName() ( return ".var_export($entity_table_name, true)."; ) funzione statica pubblica getMap() ( return ".var_export($fieldsMap, true)."; ) funzione statica pubblica getHighloadBlock() ( return ".var_export($hlblock, true)."; ) ) "; eval($eval); ) // quindi configura e allega i campi /** @var BitrixMainEntityDataManager $entity_data_class */ $entity = $entity_data_class::getEntity(); $uFields = $USER_FIELD_MANAGER->getUserFields("HLBLOCK_ ".$hlblock["ID"]); foreach ($uFields come $uField) ( if ($uField["MULTIPLE"] == "N") ( // basta aggiungere un singolo campo $field = $USER_FIELD_MANAGER->getEntityField ($uField, $uField["FIELD_NAME"]); $entity->addField($field); foreach ($USER_FIELD_MANAGER->getEntityReferences($uField, $field) come $riferimento) ( $entity->addField($riferimento ); ) ) else ( // crea entità utm static::compileUtmEntity($entity, $uField); ) ) return EntityBase::getInstance($entity_name); )

    Al diavolo, ma questi stessi blocchi ad alto carico non possono in alcun modo fungere da alternativa ai normali blocchi di informazioni. Si scopre che sono stati inventati solo per archiviare dati di riferimento non gerarchici. Inoltre, il modulo non supporta ancora le funzioni necessarie nel pannello di amministrazione, come il filtraggio in base a un campo come "Data", è impossibile chiamare l'entità HLblock con un nome comprensibile dall'uomo in modo che l'amministratore non si spaventi ogni volta che si accede alla pagina di modifica dell'entità, ad esempio BrandReference. Tutto ciò suggerisce che questa cosa è stata concepita come un'alternativa ai lenti blocchi di informazioni, ma non hanno avuto il tempo di finirla (o non ci sono riusciti, o sono andati contro gli interessi dell'azienda), e alla fine hanno rilasciato la funzionalità semilavorata come nuova funzionalità, e gli esperti di marketing l'hanno pettinata e fatta sembrare bella, hanno avuto questa idea.

    C - Controller o componente

    Un componente normale di Bitrix può essere paragonato ai widget di Yii. Questo è un certo contenitore, separato da tutti gli altri contenitori, che prende alcuni parametri come input, fa del lavoro e con il risultato del lavoro collega la vista. Gli sviluppatori Bitrix sono profondamente convinti che i componenti che forniscono immediatamente risolvano la maggior parte dei problemi affrontati dai loro colleghi. Ma, come al solito, agli sviluppatori non piace sempre nulla e le capacità dei componenti standard sono sempre "un po'" carenti. Pertanto, Bitrixoids ha deciso di dare agli sviluppatori l'opportunità di modificare il risultato di un componente... utilizzando una vista. Nella directory del modello del componente, puoi creare un file result_modifier.php, in cui puoi aggiungere i tuoi dati al risultato del componente. E se improvvisamente desideri utilizzare questi dati in un altro modello, dovrai copiare e incollare questo file (o includere questo file da un altro modello). Sono sempre stato tormentato dalla domanda: a cosa serve questo pathos? Perché non aggiungere un sacco di richieste direttamente nel modello PHP? La differenza risulta essere piccola.
    Di cosa sto parlando di modelli nella sezione sui controller...

    Bitrix ha 2 tipi di componenti 2.0 (ciao marketing di nuovo): regolari e complessi. Un componente comune è un widget. Un componente complesso è una sorta di controller + router che, in base all'URL, capisce quale pagina con una serie di widget deve essere visualizzata. La procedura operativa è più o meno questa:

    • l'URL dice /catalog/bolshaya-zelenaya-shapka.html
    • utilizzando mod_rewrite, Bitrix capisce che per la sezione fisica /catalog bisogna sempre includere il file /catalog/index.php
    • il componente complesso analizza l'URL e capisce che è necessario collegare una pagina di prodotto dettagliata, chiamiamola dettaglio
    • un componente complesso raccoglie i parametri necessari per il funzionamento dei suoi componenti figli
    • un componente complesso collega il suo template detail.php, all'interno del quale è specificata la connessione dei componenti regolari figli

    Non sembra molto carino, ma può funzionare. Tuttavia, non tutto è così semplice... Se si modificano i parametri di un componente complesso utilizzando un editor visivo, il file con le impostazioni di indirizzamento (urlrewrite.php) verrà sovrascritto dal sistema. Inoltre, se all'improvviso hai scritto qualcosa di sbagliato lì per altre pagine, qualcosa si romperà sicuramente senza alcun preavviso. In pratica ciò può comportare la perdita di funzionalità di intere sezioni del sito.
    L'impostazione dei parametri di un componente complesso può essere un compito ingrato. Uno di questi componenti può facilmente avere centinaia di parametri di input, semplicemente perché è necessario configurare i parametri dei componenti figlio.
    Un componente complesso: sembra essere un router. Tuttavia, tutti i percorsi creati in questo componente non verranno inclusi nel file sitemap.xml generato automaticamente. Questi collegamenti non saranno inclusi nel modulo di ricerca. Non avrai alcun modo per generare l'indirizzo del percorso dall'esterno (ad esempio, vuoi inserire un collegamento alla pagina dettagliata di un marchio da qualche parte nella barra laterale e non potrai chiedere al router di generare questo URL ).

    In generale, nessuno svolge realmente le funzioni di un router in Bitrix. Negli infoblock è possibile configurare il modello URL per la pagina dell'infoblock, la pagina della sezione dell'infoblock e la pagina dell'elemento dell'infoblock. Questo è tutto, gli infoblock non possono più avere pagine.
    Per i forum è possibile personalizzare i template di alcune pagine. Può essere personalizzato per i blog. Magari qualcosa si può configurare altrove... tutto questo è così decentralizzato che diventa piuttosto difficile metterlo insieme.

    I componenti regolari sono entità leggermente più semplici dei componenti complessi. Il loro compito è prendere una serie di parametri come input, elaborarli, alimentare il risultato del lavoro nel modello e memorizzare tutto nella cache.
    Tutta la logica dei componenti è contenuta nel file componente.php. Con la versione 12 di Bitrix (attualmente è attuale la versione 16, sono passati 4 anni), è diventato possibile “utilizzare l’OOP” nei componenti. Questa innovazione è che invece di un file componente.php, puoi creare un file class.php, in cui, invece dei soliti noodles, puoi scrivere una classe ereditata da CBitrixComponent. E questo è stato un grande passo avanti, perché... è diventato possibile ereditare i componenti e non utilizzare affatto result_modifier.php e non esercitarsi nel copia-incolla se all'improvviso è necessario personalizzare notevolmente il componente. Ma anche qui non va ancora tutto così bene. Dell'intero set di componenti, solo il 25-30% può vantarsi di avere una classe nel proprio arsenale. Inoltre, una buona metà di loro semplicemente non ti darà l'opportunità di espanderti completamente, perché... Spesso sono scritti in modo illogico.
    A proposito, brave persone stanno cercando di standardizzare, in qualche modo aiutano gli sviluppatori a scrivere componenti e esiste un toolkit corrispondente

    V - Visualizza o modelli

    I modelli in Bitrix possono essere suddivisi in diversi tipi:

    • Modelli di componenti 2.0 regolari e complessi
    • Modelli di siti web
    • Modelli per altre entità (mailing, newsletter, moduli web, generatori di esportazioni e molto altro)

    I modelli di componenti hanno anche la possibilità di utilizzare motori di modelli. In linea di principio, puoi connettere qualsiasi motore di template, ma non ci sono strumenti ausiliari pronti all'uso. Se qualcuno ne avesse bisogno, ho un paio di link alle prolunghe per ramoscello e lama, che funzionano e sono abbastanza utilizzate in produzione. Ma anche qui i Bitrixoidi si sono pervertiti. Il motore dei modelli può essere utilizzato solo con i componenti. Non sarà possibile connettere il motore dei template al renderer dei template del sito web o ad altre entità, perché non c'è nessun renderer lì.

    Un'altra cosa fastidiosa dei modelli di componenti è il loro posizionamento. Il componente è collegato utilizzando un design semplice

    $APPLICAZIONE->IncludeComponent("bitrix:catalog.section", "template_name", );

    Il secondo parametro è il nome del modello del componente. Pertanto, a seconda delle varie condizioni, la posizione di questo modello potrebbe trovarsi nei luoghi più inaspettati:

    • 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

    E non ho ancora elencato tutte le opzioni...

    Un template di sito può essere considerato come un insieme di file: header.php, footer.php (sì, il sito deve averli), description.php (descrizione del sistema del template del sito), template_styles.css (stili del template del sito), directory con modelli di componenti e un altro gruppo di file meno significativi. È tutto. E non puoi influenzarlo in alcun modo, non puoi farci nulla. È impossibile utilizzare il motore del modello.

    Non c'è niente da dire sugli altri modelli. O vengono semplicemente archiviati nel database sotto forma di layout con alcuni dati "variabili" inclusi al suo interno, oppure si tratta di uno stupido file PHP che fa tutto il lavoro, dal recupero dei parametri dal database alla visualizzazione delle informazioni. Ad esempio, puoi guardare il generatore di file YML per il mercato. Non ha senso pubblicarlo qui, semplicemente perché è piuttosto grande, circa 2k righe. Chi ne ha bisogno può cercarlo su Google, è in /bitrix/modules/catalog/load/yandex_run.php

    Natura dell'archivio

    Come è diventato chiaro sopra, l'architettura in Bitrix non è molto buona. Ma Bitrix ha anche un altro aspetto importante della sua architettura.
    Bitrix è un CMS per metà file. Molte cose vengono controllate utilizzando alcuni file:

    • Hai bisogno di una pagina: crea un file
    • Hai bisogno di una serie di pagine: crea un file e collega lì un componente che funzioni con gli infoblock
    • Devi impostare un titolo per la pagina: modifica il file
    • Devi impostare un titolo per tutte le pagine di una sezione: crea un file speciale.section.php nella radice di questa sezione
    • Devi modificare i diritti: modifica il file .access.php
    • Impostazioni prima dell'inizializzazione del sistema - nel file dbconn.php, .settings.php e .settings_extra.php
    • result_modifier.php, componente_epilog.php, init.php, .parameters.php, .description.php ....

    E ci sono un numero enorme di file speciali sparsi in Bitrix. Da un lato, ciò offre una certa flessibilità quando si lavora con il sistema. D'altra parte, questo può trasformarsi in un tormento sia per lo sviluppatore che per il gestore del sito. I file di pagina a volte si trasformano in un caos di codice PHP, layout e componenti plug-in. Di conseguenza, l'editor visivo potrebbe analizzare questo file in modo errato e, durante la modifica, potrebbe facilmente sfuggire ai tag PHP in alcuni punti, il che porterà la pagina a non funzionare. Dici: non è necessario scrivere codice PHP in tali file? Sì, lo so. Ma Bitrix molto spesso e senza alternative ti obbliga a farlo.
    E devi tenere costantemente a mente le informazioni su che tipo di file sono e quali dati possono contenere. File diversi dovrebbero contenere dati diversi con strutture diverse ed è necessario ricordarli per ciascuna opzione. Cercarlo ogni volta nella documentazione è un duro lavoro.

    In aggiunta a quanto sopra

    Puoi lamentarti all'infinito di quanto tutto funzioni male in Bitrix. A mio parere, tutte queste lamentele possono essere caratterizzate da una frase: "in qualche modo non del tutto". E in effetti, se i Bitrixoid annunciano improvvisamente qualche tipo di funzionalità, in qualche modo non la rilasciano completamente, non la finiscono, non la ricordano. Ci sono moltissimi esempi:

    • ORM implementato: non ancora terminato, non può essere utilizzato completamente
    • Abbiamo realizzato un caricatore automatico, funziona solo nei moduli e non secondo gli standard
    • ha reso possibile connettere un motore di template, ma non puoi usarlo ovunque e non completamente
    • eccetera. e così via.

    In poche parole, cercherò di caratterizzare i restanti problemi che devo affrontare ogni giorno.

    Ammin

    Se qualcuno ha lavorato con il pannello di amministrazione, ha creato le proprie pagine nella parte amministrativa nel modo in cui Bitrix suggerisce di farlo, mi capirà. E' semplicemente l'inferno. Per coloro che non lo sanno, Bitrix suggerisce di utilizzare un file noodle per ogni pagina. Ad esempio, la pagina per la visualizzazione dettagliata di un ordine nel pannello di amministrazione realizzata dagli sviluppatori Bitrix occupa più di 4k righe. Il mio IDE inizia a rallentare durante la visualizzazione del contenuto di questo file. Lì hai php, js e html. È positivo che si siano sbarazzati di SQL, anche se sono sicuro che si trovi su altre pagine amministrative.
    E cosa impedisse alle pagine amministrative di funzionare utilizzando gli stessi componenti non è chiaro. Semplicemente non esiste alcun modo per personalizzare la maggior parte delle pagine amministrative. Nel caso dei componenti, ciò potrebbe essere fatto in pochissimo tempo.
    A proposito, brave persone hanno creato un modulo che ti aiuterà a creare pagine amministrative

    js

    Bitrix ha un componente js che funge da sorta di framework client. A nessuno degli sviluppatori piace per diversi motivi:

    • non è quasi documentato
    • è mostruoso
    • duplica in gran parte la jquery familiare a molti

    Bitrix lo utilizza molto spesso nei suoi componenti, provocando così ancora più rabbia tra gli sviluppatori. Il nucleo di questa libreria in forma ridotta è 85kb, il che non è piccolo. Non puoi evitare di collegarlo se desideri utilizzare tutte le funzionalità di Bitrix (composito, gestione patrimoniale).

    Spirito copia-incolla

    Ultimamente, sempre meno spesso, ma comunque abbastanza spesso, Bitrix ti obbliga a copiare e incollare qualcosa. Se vuoi modificare il funzionamento di un componente, copialo e incollalo. Se desideri creare il tuo modello di caricamento, copia e incolla quello di sistema e completalo. Se vuoi creare quasi lo stesso modello che hai, copialo, incollalo e modificalo leggermente. E ne parlano anche nei corsi per sviluppatori alle prime armi. Non ho parole.

    Asset management e CDN

    Mi piace molto il modo in cui Bitrix gestisce le risorse. In linea di principio è possibile registrare un insieme di "biblioteche" specifiche. Ogni libreria è un insieme di file css/js, che possono dipendere da altre librerie. Se colleghi una libreria a una pagina, prima di collegarla, tutte le dipendenze verranno risolte e tutte le librerie dipendenti verranno inserite nella pagina. Sembra che tutto vada bene, solo che ogni risorsa verrà inserita come file separato nello script o nel tag di collegamento. E grazie a questo ci sono siti che hanno 30-50 script e lo stesso numero di file di stile collegati.
    È una domanda di merda, hanno detto in Bitrix, e hanno messo un segno di spunta magico che unisce tutti questi file in uno solo. E sono comparsi siti in cui invece di 50 script ce n'erano 2, ciascuno da 300-500 kb. Qualche tempo fa, questa fusione funzionava con errori e univa le stesse risorse più volte, ma ora sembra che il problema sia stato risolto.
    E poi Bitrixoids si è tolto di mezzo: ha aggiunto la possibilità di caricare tutte le risorse su un server CDN. Che cade sempre...
    Poi è apparso Google Pagespeed Insights, che consigliava di spostare tutte le risorse in fondo alla pagina. E in Bitrix hanno creato di nuovo una casella magica che omette stupidamente tutte le risorse nel corpo se non sono contrassegnate con un attributo speciale.
    Insieme alla casella distribuiscono anche versioni minimizzate dei loro script, che vengono collegati quando si utilizza un'altra casella di controllo magica nel pannello di amministrazione.
    In generale, niente SCSS per te, niente TypeScript. Se vuoi gestire le risorse in modo competente, non utilizzare il sistema Bitrix integrato, utilizza il webpack, che può essere facilmente accoppiato con Bitrix.

    Multisito/multilingue

    Questo è probabilmente il peggior grattacapo per uno sviluppatore, che dura fin dalla nascita del prodotto. Non puoi semplicemente andare avanti e creare un sito web multilingue. E se hai bisogno di un catalogo multilingue con prezzi e valute diverse, allora questo si trasforma in farina, per la quale dovrai anche pagare una bella somma (dovrai sborsare per l'acquisto di una licenza aggiuntiva per la prossima versione linguistica del luogo).
    Se stai creando un sito Web multilingue e multivaluta, preparati al fatto che Bitrix resisterà in modo molto aggressivo. Le impostazioni multisito sono decentralizzate in tutto il pannello di amministrazione. Ogni entità nel pannello di amministrazione ha la propria dipendenza dalla versione linguistica del sito. Alcune entità potrebbero non supportare affatto le dipendenze sito/lingua, mentre altre hanno solo un legame inequivocabile con la lingua, quindi questa entità dovrà essere duplicata e quindi supportata.
    Nella versione base, per far funzionare un blocco informativo in più lingue, sarà necessario creare un duplicato di questo blocco informativo. Ma in pratica, nessuno lo fa e cerca di trovare i propri modi per archiviare un'entità a livello centrale, distribuendo i suoi attributi dipendenti dalla lingua ad altre strutture di archiviazione.
    Non è possibile impostare la lingua predefinita durante la localizzazione. Se hai una variabile di lingua che descrive qualche frase in russo, e questa variabile di lingua non è nella versione inglese, allora sul sito inglese verrà mostrata una riga vuota e questo non può essere influenzato in alcun modo (in molti casi potresti lascia la frase russa in modo che non ci siano vuoti).

    Meccanismo di gestione dei diritti

    Erano molto intelligenti con questo sottosistema. Spesso è difficile capire perché hai concesso i diritti di visualizzazione a qualche entità, ma l'utente non può utilizzarli. Ad esempio, per dare il diritto di modificare un blocco di informazioni, è necessario fornire l'accesso alla directory /bitrix/admin, concedere i diritti per un blocco di informazioni specifico e concedere i diritti nel modulo principale. È necessario eseguire troppe operazioni per concedere diritti a un'entità. E se i diritti non sono sufficienti, senza curiosare nel codice sorgente non c'è modo di capirne il motivo.

    Configurazione

    Bitrix non dispone di un hub centralizzato che consenta di gestire le impostazioni di sistema. Le impostazioni sono ancora una volta decentralizzate in tutto il sistema. Le opzioni sono disponibili nelle impostazioni del modulo, nelle impostazioni del componente, in COption (non posizionate nel pannello di amministrazione). Nel pannello di amministrazione, le opzioni per un modulo possono essere distribuite su 3-4 pagine diverse, che si trovano in posti completamente diversi. urlrewrite può essere modificato tramite il pannello di amministrazione! Ora anche .settings e .settings_extra. A volte non è del tutto chiaro quale di esse abbia la priorità più alta, molto spesso non ci sono sufficienti spiegazioni per le opzioni e le relazioni non sono chiare. Non esiste un modo nativo per condividere la configurazione tra gli sviluppatori.
    Le impostazioni possono essere molto illogiche. A volte si arriva al punto di assurdità... guarda il componente bigdata: una persona inesperta può configurarlo?

    Integrazione con 1C

    Questo è l'elemento nell'elenco delle funzionalità di Bitrix di cui si innamora un numero abbastanza elevato di clienti. Bitrix promette di impostare l'integrazione bidirezionale del sito con 1C in 2 clic, che fornirà immediatamente contenuti e documenti da un sistema all'altro.
    Sì, lo è davvero, ma con diversi avvertimenti.
    In primo luogo, per eseguire l'integrazione "out of the box" senza ulteriori sforzi, devi fare tutto esattamente come scritto nella documentazione di Bitrix: creare un catalogo sul sito secondo le regole offerte da Bitrix e creare il catalogo in 1C che Bitrix richiede. Idealmente, crea tutto da zero e poi forse tutto funzionerà per te fuori dagli schemi.
    In secondo luogo, Bitrix non è compatibile con tutte le configurazioni 1C pronte all'uso. Vale la pena dare un'occhiata prima
    In terzo luogo, non esiste un mondo ideale. In genere, un cliente che desidera un sito Web ha già un'attività di vendita al dettaglio, il che significa che ha già 1C, che è un'enorme discarica. E questa spazzatura deve essere gettata sul sito. E affinché il sito non risulti essere la stessa spazzatura, il meccanismo di scambio deve essere notevolmente migliorato.
    Molto spesso, le esigenze del cliente differiscono notevolmente dalla visione del prodotto che il team Bitrix ha formato, e quindi perfezionare il meccanismo di scambio può essere piuttosto costoso, paragonabile in termini di intensità di lavoro allo sviluppo di un modulo di scambio unico per un caso specifico.
    Pertanto, non illuderti che sarai in grado di integrare facilmente il tuo sito con 1C. Queste sono tutte le macchinazioni degli esperti di marketing.

    Anche il perfezionamento dello scambio con 1C è un argomento a parte. La classe CIBlockCMLImport è responsabile dell'organizzazione dello scambio di cataloghi - 5,7k righe. Uno dei metodi principali che molto spesso richiede l'estensione è CIBlockCMLImport::ImportElement, che contiene più di 1k righe. È sufficiente ereditarlo una volta, aggiornare il prodotto un paio di volte per un lungo periodo di tempo e puoi ottenere uno scambio non funzionante con 1C. Pertanto, gli sviluppatori spesso non si preoccupano di questa classe e cercano in qualche modo di entrare nel processo di importazione utilizzando i gestori di eventi. Anche lavorare con i gestori di eventi in Bitrix, soprattutto nel modulo infoblocks, non è un'esperienza molto piacevole, se non altro perché gli eventi dello stesso tipo non sono disposti in modo uniforme e alcuni eventi semplicemente non sono sufficienti.
    In generale, le cose sono tristi come prima.

    Incoerenza

    A volte mi sembra che gli sviluppatori di diversi moduli non comunichino realmente tra loro. Studiando i sorgenti del kernel, ti imbatti in soluzioni molto eterogenee che potrebbero essere implementate su un motore, ma per qualche motivo sono implementate in modo diverso.
    Ad esempio, puoi prendere le proprietà degli elementi del blocco informazioni e degli UserField. Entrambe le entità sono infatti un campo aggiuntivo per un'altra entità. Ha un tipo, un significato e una descrizione. Il valore è memorizzato in una o più tabelle separate del database; hanno un'interfaccia di accesso ai dati più o meno simile. Allora perché non creare per loro la stessa interfaccia?
    Alla fine di marzo il modulo di vendita è stato aggiornato all'ultima versione e sono state promesse anche proprietà arbitrarie per gli ordini. Esiste davvero una nuova, terza interfaccia per lavorare con le proprietà estese delle entità?

    Bitrix24

    Questo è generalmente un argomento di discussione separato. Spesso si crea confusione a causa di questo sistema. Sono disponibili 2 opzioni per B24: SaaS e Standlone. Esiste un marketplace per B24, ma contiene applicazioni solo per la versione SaaS! Se disponi di una versione in scatola, acquistata per 200.000, non sarai in grado di installare applicazioni popolari come il designer di documenti e in generale non sarai in grado di installare alcuna applicazione dal marketplace per Bitrix24 sul tuo Bitrix24. Questo è un vero paradosso.
    Invece, il marketplace della versione normale sarà disponibile nel tuo Bitrix24. Esistono molte altre soluzioni, ma si concentrano principalmente sulla gestione del sito e non su B24.

    Bitrix24, come mi è stato detto nel reparto di supporto tecnico, è un sistema olistico. Se interferisci con il funzionamento dei componenti standard del sistema, preparati che questa funzionalità verrà interrotta con gli aggiornamenti successivi. Bitrix non conterà su di te per finalizzare i componenti del portale, e questo nonostante rimandino ufficialmente i loro clienti ai partner

    A proposito, modificare i componenti nella versione boxata di B24 è un compito piuttosto impegnativo. Componenti che generano codice js che, utilizzando ajax, accede al codice php, che genera html+js in risposta. Questa è una miscela infernale in cui davvero non vuoi tuffarti.

    Documentazione

    La documentazione di Bitrix è in ritardo rispetto allo sviluppo del prodotto di 1-1,5 anni. Il codice è coperto molto poco da phpDocs e spesso il commento prima della classe è solo per spettacolo, essendo generato automaticamente nell'IDE.
    Lo stile stesso di presentazione della documentazione nelle fonti ufficiali è spesso troppo “libero” e il contenuto di alcuni articoli nella documentazione potrebbe non avere nulla a che fare con Bitrix stesso.
    Il corso per sviluppatori contiene molte informazioni, ma il formato in cui lo sviluppatore viene introdotto alle funzionalità del sistema non fornisce il livello di percezione richiesto. Se vai al Symfony Cookbook, lì è tutto spiegato, tutti gli aspetti necessari sono descritti a seconda della versione. Mentre in Bitrix il corso di formazione per sviluppatori contiene in modo poco chiaro informazioni strutturate sul vecchio e sul nuovo kernel, che vengono presentate prima separatamente e poi miste, il che fa venire il mal di testa ai principianti.

    Organizzazione del processo di sviluppo

    A causa della specificità del sistema, non è così facile organizzare un processo di sviluppo conveniente. Non l'ultima versione dell'edizione Business (che era a portata di mano) dopo l'installazione occupa, pensateci, quasi 530 megabyte

    $ du -s *|sort -nr|cut -f 2-| while read a;do du -hs $a;done 523M bitrix 204K upload 64K bitrixsetup.php 56K desktop_app 20K readme.html 20K License.html 4.0K web . configurazione 4.0K urlrewrite.php 4.0K readme.php 4.0K licenza.php 4.0K install.config 4.0K indice.php

    Di questo volume, una buona metà sono binari e programmi di installazione, che in generale non sono necessari per il controllo della versione. In generale, è consuetudine non eseguire la versione del core Bitrix. Gli stessi sviluppatori Bitrix garantiscono l'integrità del core e gestiscono le dipendenze delle versioni dei diversi moduli durante gli aggiornamenti. Ma questo comporta immediatamente almeno un grosso svantaggio: è impossibile distribuire un progetto completamente funzionante con un team del controllo versione; devi assemblarlo in parti: prendi i sorgenti del kernel dal backup Bitrix e i sorgenti degli sviluppatori da git .
    Anche con la base le cose non vanno bene. Se tu stesso puoi utilizzare le migrazioni durante lo sviluppo, Bitrix inserisce gli aggiornamenti nel database utilizzando script ordinari che non puoi controllare. Pertanto, durante l'aggiornamento, dovrai comunque trasferire i backup del database dall'host di sviluppo centrale ad altri sviluppatori.
    Le brave persone, ancora una volta, sono strumenti taglienti che aiutano a organizzare tutto questo, ma sfortunatamente non è ancora possibile costringere Bitrix a seguire queste regole.
    Ufficialmente Bitrix ti consente di avere 2 copie di una distribuzione. Uno è per la produzione, il secondo è per lo sviluppo. Se hai più sviluppatori su un progetto, allora sei, per così dire, un fuorilegge) In effetti, è sufficiente interrompere sulla macchina con Bitrix le connessioni in entrata e in uscita da/a www.bitrixsoft.com, e poi tu puoi rivettare quante copie dello sviluppo desideri, semplicemente non saranno in grado di aggiornarsi.

    Colleghi

    E l'ultima domanda su cui vorrei toccare.
    Dato che Bitrix ha una bassa barriera all’ingresso, tra le aziende che forniscono servizi in questo mercato c’è molto personale non qualificato. Ho visto molti progetti diversi durante la mia carriera (più di un centinaio in totale) completati su 1C-Bitrix. Posso dire con sicurezza che il 95% di essi sono stati eseguiti in maniera “schifosa”. Molto raramente ci siamo imbattuti in progetti il ​​cui sviluppo avesse un senso di approccio, ma questi erano solo alcuni. Tutto questo è molto triste.

    conclusioni

    Naturalmente, tutti gli svantaggi non possono essere considerati in un unico articolo. Ogni giorno ti imbatti in alcune piccole cose che interferiscono con il tuo lavoro quotidiano. Ma è semplicemente impossibile considerare tutte queste piccole cose, e probabilmente non ce n'è bisogno.

    Quali conclusioni si possono trarre qui? Bitrix è un sistema estremamente complesso perché ha un'architettura mal concepita e molti difetti che continuano a vivere nel prodotto per molto tempo. D'altra parte, Bitrix è un sistema abbastanza semplice che richiede un livello di qualifica molto inferiore per iniziare, a differenza dei framework.
    Supportare questo prodotto è un compito davvero ingrato rispetto a prodotti come Symfony, Laravel, Yii. Al prodotto piace davvero mettere i bastoni tra le ruote sia agli sviluppatori inesperti che a quelli esperti, il che, a sua volta, può riflettersi nel costo dei servizi degli sviluppatori Bitrix esperti.

    Mi pento di aver passato così tanto tempo a lavorare con questo sistema? Piuttosto sì che no. Sarebbe più saggio dedicare questo tempo allo studio di qualcosa di più corretto e più logico (cosa che sto cercando di fare attivamente ora). Ma è successo che all'inizio del mio viaggio non c'era nessuno che mi guidasse nella giusta direzione.

    Se sei uno sviluppatore PHP principiante, preferirai studiare framework come Symfony, Laravel, Yii, ZendFramework rispetto a Bitrix. Credetemi, sarà più che ripagato in futuro. Avendo padroneggiato uno qualsiasi di questi framework, non sarà difficile per te sviluppare qualcosa per Bitrix in futuro. Se non hai scelta, allora studia Bitrix, ma nel tempo libero è meglio provare comunque ad immergerti nel mondo dei framework per mettere a posto il tuo cervello.

    Se sei uno sviluppatore con esperienza in Bitrix, ma senza esperienza in altri framework, assicurati di immergerti in un altro mondo; scoprirai molte conoscenze nuove e utili che ti aiuteranno a scrivere soluzioni molto migliori per 1C-Bitrix. Prova ad utilizzare soluzioni di altri framework nei tuoi progetti, fortunatamente questo non è difficile grazie all'approccio componente di quest'ultimo e del compositore.

    Se sei un cliente, non fidarti degli operatori di marketing Bitrix. Niente sarà così facile come dicono nelle presentazioni Bitrix. E non incolpare i tuoi sviluppatori per questo, non hanno niente a che fare con questo. Se desideri creare un negozio online ampio e complesso a livello Eldorado/Mvideo/Sportmaster, Bitrix potrebbe non essere la scelta migliore.

    Spesso durante lo sviluppo è necessario selezionare ed eliminare alcune parti dei dati contrassegnati per la cancellazione. Questa esigenza riguarda soprattutto i database di informazioni di grandi dimensioni con decine o centinaia di migliaia di oggetti contrassegnati per la cancellazione. L'elaborazione di tutti i dati contemporaneamente richiede molto tempo, quindi senza uno strumento pratico in cui è possibile visualizzare, selezionare ed eliminare chiaramente alcune parti dei dati, è impossibile.

    Questa elaborazione è il risultato del mio lavoro durante un certo periodo di tempo. La funzionalità è stata ampliata secondo necessità. L'elaborazione funziona in modalità applicazione normale e nel Thick Client di un'applicazione gestita. Implementato nell'elaborazione:

    • visualizzazione di un albero di metadati con la possibilità di contrassegnare gli oggetti del database che devono essere eliminati;
    • visualizzazione del numero di oggetti contrassegnati per la cancellazione, contrassegnati dall'utente e possibili per la cancellazione;
    • possibilità di abilitare/disabilitare la modalità operativa esclusiva;
    • è possibile cancellare le registrazioni correlate dei registri informativi indipendenti;
    • possibilità di sostituzione di gruppo dei collegamenti trovati;
    • visualizzando i collegamenti all'oggetto da eliminare sotto forma di albero. Riflessione in questo albero della struttura dei collegamenti ciclici (ricorsivi), evidenziando i collegamenti chiave (che impediscono la cancellazione dell'oggetto);
    • eliminare un pacchetto di collegamenti ciclici in una transazione - "o tutto o niente", in modo che "Oggetto non trovato..." nel database;
    • possibilità di query di selezione arbitrarie per ciascun oggetto di metadati. In questo caso è possibile copiare le richieste di selezione. Ciò è utile, ad esempio, quando è necessario eliminare documenti per un determinato periodo per un'organizzazione; o libri di consultazione su qualsiasi criterio di selezione;
    • conservazione e restauro delle selezioni precedentemente utilizzate;
    • indicatore di avanzamento nella ricerca di oggetti contrassegnati per la cancellazione e monitoraggio della referenzialità con possibilità di interrompere l'operazione.

    Per fornire questa funzionalità, l'elaborazione utilizza attivamente la RAM. E, sebbene siano state adottate misure per utilizzare la RAM in modo ottimale, in alcuni casi (database di grandi dimensioni, un gran numero di oggetti eliminati) può verificarsi una situazione di mancanza di memoria. In questi casi è necessario limitare l'elenco degli oggetti da eliminare o passare all'utilizzo del client 1C a 64 bit.

    Ultima versione 1.14. I cambiamenti:

    • I ritardi nelle transizioni client-server su grandi quantità di dati sono stati ridotti;
    • Logica migliorata per le configurazioni con piani dei conti senza tipi di sottoconti assegnati .

    Versione 1.13. I cambiamenti:

    • Risolto errore mobile della procedura del modulo durante il tentativo di eliminazione;
    • Nella forma normale è stata aggiunta la possibilità di contrassegnare i collegamenti correlati per l'eliminazione. Per contrassegnare per l'eliminazione, seleziona una o più righe di collegamenti correlati e seleziona "Segna per l'eliminazione" dal menu contestuale. Il blocco dei collegamenti correlati visualizza un albero. Quando si seleziona una riga nell'albero e si richiama il segno per l'eliminazione, gli elementi di tutte le righe figlie vengono contrassegnati per l'eliminazione.
    • Nella forma regolare è stata aggiunta la funzione "Mostra in lista" del relativo albero dei link.

    Elaborazione della versione 1.12. I cambiamenti:

    • Risolto un errore non critico nel modulo abituale che si verifica durante l'aggiornamento della visualizzazione dei collegamenti correlati.

    Elaborazione della versione 1.11. I cambiamenti:

    • Risolto un errore nella colorazione delle chiavi associate alle voci del registro delle informazioni.

    Elaborazione della versione 1.10. I cambiamenti:

    • È stato restituito il meccanismo d'azione dell'opzione "Elimina record dei registri informativi". Questa funzionalità ora si applica ai record dei registri di informazioni in cui l'oggetto da eliminare non è il principale. I record con un oggetto eliminato iniziale vengono eliminati automaticamente;
    • Registrazione/eliminazione disabilitata con il flag Data Exchange.Loading;
    • Nell'albero dei collegamenti è stata aggiunta la visualizzazione dell'attributo "Pubblicato" e "Flag di cancellazione" per gli oggetti collegati. Nella forma normale - sotto forma di un'immagine di linee, nella forma gestita - sotto forma di caselle di controllo.

    Elaborazione della versione 1.09. I cambiamenti:

    • È stato corretto un errore di battitura nell'algoritmo nel controllo della referenzialità delle directory proprietarie;
    • È stata modificata una sezione di codice di un form gestito che non ne permetteva il lancio su una delle configurazioni standard;
    • Aggiunto controllo sulle parti tabulari standard degli oggetti di configurazione.

    Elaborazione della versione 1.08. I cambiamenti:

    • Lavoro di elaborazione implementato nel Thick Client di un'applicazione gestita.

    Elaborazione della versione 1.07. I cambiamenti:

    • È stato implementato un meccanismo di monitoraggio/sostituzione dei riferimenti per le dimensioni fuori bilancio dei registri contabili.

    Elaborazione della versione 1.06. I cambiamenti:

    • La cancellazione dell'albero dei metadati dopo la ricerca è stata disabilitata: ora l'albero dei metadati è sempre visualizzato;
    • Aggiunto meccanismo di sostituzione del collegamento.
    • Aggiunta visualizzazione del numero totale di oggetti presenti nel database;
    • Il meccanismo di eliminazione passa alla modalità Scambio Dati. Loading = True;
    • L'opzione "Elimina voci del registro dettagli" ora funziona per le dimensioni iniziali e non iniziali.

    La guida all'elaborazione è cambiata:

    Il trattamento si compone di due parti: Albero degli oggetti eliminati (3) E Albero dei collegamenti (4) all'oggetto da eliminare.

    Nell'albero degli oggetti eliminati vengono effettuate le impostazioni di base per la ricerca dei collegamenti eliminati. L'albero dei collegamenti analizza quelli relativi agli eliminati

    oggetto altri oggetti della base di informazioni.L'elaborazione è controllata utilizzando Menu azioni di base (1), segni nell'albero

    oggetti da eliminare e Menù funzioni aggiuntive (2)

    1. Menu Azioni di base

    -Chiaro- inizializza l'albero degli oggetti metadati, cancella i risultati ottenuti in precedenza;

    -Trovare- cerca gli oggetti della base informazioni contrassegnati per la cancellazione utilizzando gli oggetti contrassegnati. La ricerca viene effettuata utilizzando directory,

    Documenti, piani contabili, piani di tipologie di caratteristiche, piani di tipologie di calcolo, piani di scambio, attività, processi aziendali;

    -Sostituzione- sostituisce il link eliminato con quello indicato nella colonna "Sostituzione". Quando si sostituiscono i riferimenti nei dettagli, registrare le dimensionipotrebbe verificarsi una situazione

    Quando il set risultante contiene righe duplicate lungo le dimensioni, in tali situazioni le righe duplicate vengono eliminate. In questo caso viene data priorità alla riga con il numero più basso in ordine.

    -Controllo- monitora la natura di riferimento degli oggetti del database trovati contrassegnati per l'eliminazione. Il controllo viene effettuato in base a dettagli, misurazioni,

    Risorse di oggetti di metadati, dettagli di parti tabulari di oggetti di metadati, tipi di calcolo dei registri di calcolo, conti e analisi dei registri contabili,

    Compiti principali dei processi aziendali,misurazioni della sequenza dei documenti.

    -Eliminare- cancella gli oggetti contrassegnati che possono essere cancellati.

    2. Menù funzioni aggiuntive

    -Selezioni- un sottomenu con operazioni per la selezione di un albero di oggetti (ulteriormente).

    -Eliminare le voci del registro delle informazioni- abilitare/disabilitare la modalità di cancellazione dei record relativi ai registri informativi indipendenti.

    -Visualizza l'elenco degli eliminati- quando questa opzione è abilitata, durante l'eliminazione, nei messaggi di sistema verrà visualizzato l'elenco degli oggetti eliminati;

    -Modalità monopolio- abilitare/disabilitare la modalità esclusiva di funzionamento del database;

    -Trova e rimuovi- vengono eseguite tre azioni consecutive sugli oggetti metadati selezionati: trova - controlla - elimina.

    3. Albero degli oggetti

    Nel suo stato iniziale, rappresenta un elenco completo di oggetti di metadati.È necessario impostare i contrassegni per tali oggetti

    tra i quali verrà effettuata la ricerca di quelli contrassegnati per la cancellazione.

    Eseguendo una ricerca nella colonna dell'albero " Totale"Il numero di oggetti trovati contrassegnati per l'eliminazione si rifletterà nella colonna" Collegamenti totali" Schermo

    il numero totale di collegamenti di questo tipo di oggetto.

    Eseguendo il controllo nella colonna dell'albero " Possibile eliminare" visualizzerà il numero di oggetti che possono essere eliminati.

    Le linee dell'albero sono colorate di verde o di rosso a seconda della possibilità di eliminare gli oggetti trovati. Le righe contenenti informazioni sono evidenziate in blu

    Utilizzando il menu contestuale è possibile ordinare l'albero nell'ordine desiderato.

    Le funzioni di impostazione/deselezionazione dei gruppi operano sui filari degli alberi selezionati.

    Casella di controllo " Segno" viene utilizzato sia durante la ricerca di oggetti contrassegnati per l'eliminazione, sia durante il monitoraggio della referenzialità e la sostituzione dei collegamenti.

    In una colonna " Selezione"Per gruppi di oggetti, è possibile specificare una query personalizzata per filtrare ulteriormente gli oggetti trovati per l'eliminazione.Fare doppio clic nella colonna "Selezione".

    apre un modulo di richiesta personalizzato

    Una richiesta personalizzata consente di applicare un filtro aggiuntivo quando si selezionano oggetti contrassegnati per l'eliminazione.

    La selezione finale della richiesta deve contenere un campo denominato " Collegamento", questo campo sarà un campo filtro.

    Nel modulo del costruttore della richiesta (7) :

    -Chiaro- cancella la richiesta corrente;

    -Predefinito- genera una nuova richiesta in base al tipo di dati dell'oggetto corrente;

    -Ottieni parametri- aggiornamento dell'elenco dei parametri dalla richiesta

    Le query di filtraggio possono essere installate solo a livello del 2° raggruppamento dell'albero degli oggetti eliminati, ad es. a livello di sezione di metadati.

    4. Albero dei collegamenti correlati

    Quando si attiva una riga nell'albero degli oggetti, l'albero dei collegamenti visualizza la struttura gerarchica dei collegamenti trovati all'oggetto da eliminare.

    Quando viene rilevato un riferimento circolare di oggetti in una struttura ad albero, l'oggetto corrente coinvolto nel riferimento circolare lo è

    verrà reso ripetutamente e terminerà nell'albero di riferimento ciclico.

    Nelle righe di questo albero sono evidenziati in rosso i collegamenti che non possono essere cancellati o che non vengono cancellati secondo le impostazioni attuali;

    che non consentono l'eliminazione definitiva dell'oggetto corrente.

    Cliccando due volte nelle righe di questo albero è possibile visualizzare il collegamento associato.

    5. Menu contestuale dell'albero degli oggetti

    Il menu contestuale dell'albero degli oggetti funziona in base alle linee selezionate dell'albero.

    Azioni Disponibili:

    -Imposta/deseleziona i segni- imposta/deseleziona i filari degli alberi selezionati, compresi i filari subordinati;

    -Ordinamento in ordine crescente/discendente- gestire l'ordinamento dei filari di alberi;

    -Cancella selezione- (scorciatoia da tastiera Ctrl-X ) le query di filtro dati vengono cancellate nelle righe dell'albero selezionate;

    1. Quali contenuti informativi possono essere posizionati tra i siti?

    Utenti
    + Corso di studi
    + Blocchi di informazioni
    + Forme

    2. Quando si utilizza un sistema multisito

    + I budget utente comuni vengono utilizzati per tutti i siti
    - Vengono creati budget utente separati per ciascun sito

    Hai bisogno di una buona elettricità? Per prima cosa devi imparare a controllarlo e un dispositivo di misurazione PKE può farlo perfettamente. Il tester di energia PKE-A-S4 consente l'ispezione energetica, la misurazione e la registrazione delle quantità di energia elettrica, del consumo di elettricità, dei profili di carico e della misurazione del carico dei circuiti secondari.

    3. Se, durante la configurazione di un sito, non si specifica il nome del sito nella sezione "Parametri", allora

    + il sistema utilizzerà il nome del sito specificato nelle impostazioni del modulo Principale
    - verrà utilizzato il valore specificato nel parametro "Nome" nella sezione delle impostazioni principali
    - il valore non sarà definito

    4. Numero di siti nel sistema

    + determinato dal contratto di licenza e dalla licenza per siti aggiuntivi
    - dipende dal numero di lingue di interfaccia nel sistema
    - illimitato

    5. La configurazione delle impostazioni della lingua per la sezione pubblica del sito viene effettuata:

    Nel modulo per modificare i parametri della directory principale del sito
    - sotto forma di creazione/modifica di una lingua
    + separatamente per ciascun sito sotto forma di creazione e modifica di un sito

    6. Posso configurare più di due siti in una configurazione multisito?

    + È possibile se acquisti licenze aggiuntive per siti aggiuntivi
    - No, questo è proibito dal contratto di licenza
    - Sì, senza restrizioni

    7. Quando si attiva una licenza per 4 siti aggiuntivi (oltre ai due disponibili per l'uso di default), è possibile utilizzare il seguente numero di copie del kernel di sistema:

    3
    - 6
    + 1
    - 2

    8. Durante la visualizzazione della struttura dei file, la divisione per siti verrà effettuata nei seguenti casi:

    Organizzazione multisito su un dominio
    + rappresentazione logica della struttura
    + organizzazione di più siti su domini diversi

    9. Vengono utilizzate tecnologie per il trasferimento dei cookie degli utenti tra siti

    + se nelle impostazioni del modulo principale è attivata l'opzione "Distribuisci cookie a tutti i domini".
    - sempre se nel sistema è presente un modulo statistiche
    - se è attivata l'opzione "Estendi autorizzazione a tutti i domini".

    Imposta l'opzione appropriata nelle impostazioni dei siti
    + contrassegna l'opzione corrispondente nelle impostazioni del modulo principale
    - impostare l'opzione appropriata nelle impostazioni del Gruppo utenti

    11. Come puoi chiudere un solo sito con l'identificatore ru per gli utenti in visita?

    Utilizzando il pulsante: Impostazioni -> Modulo principale -> Procedure di servizio -> Chiudi accesso per i visitatori
    + inserendo il codice del programma speciale nel file: /bitrix/php_interface/ru/init.php

    12. Nelle impostazioni di quali moduli puoi separare i parametri per diversi siti?

    Nessuno lo consente
    - Modulo Web Form, Flusso documentale, Modulo Gestione struttura
    - Negozio online, modulo Blog, modulo principale
    - Tutti lo permettono
    + Modulo Blog, Modulo Gestione Struttura, Negozio Online

    13. Per creare siti aggiuntivi nel sistema oltre a quelli consentiti nella licenza, è necessario:

    Creare una lingua di interfaccia aggiuntiva
    - creare un account nel sistema, creare una sottodirectory separata
    + ottenere una licenza per un sito aggiuntivo, creare un account nel sistema, determinare il percorso dei file della parte pubblica del sito

    14. È possibile separare i diritti per visualizzare le statistiche di diversi siti nel prodotto:

    Sì, se si effettuano le impostazioni di accesso appropriate nel modulo Statistiche
    + No, un utente che ha il diritto di visualizzare le statistiche potrà visualizzare le statistiche per tutti i siti

    15. Separatamente per ciascun sito, è possibile configurare le seguenti impostazioni per il modulo Gestione struttura:

    + imposta i tipi di proprietà
    - organizzare un sistema di accesso alle sezioni per ciascun sito
    - scegli un editor HTML visivo e le sue impostazioni individualmente per ciascun sito
    + imposta i tipi di menu
    + definire il numero di opzioni di menu aggiuntive

    E cosa dicono, queste voci. Uno e. Stessa cosa: io sono il venditore, io sono il venditore. Cosa farai. Cosa sai fare. SÌ, Cipolla di Idra in Russia 2016. E questo significa che Bull Gates non entra in contatto. Non funziona. O forse sta arrivando. Ma tu non capisci. Forse il Minotauro è quel topo morto sul soffitto. . Può darsi.

    E non ti hanno spiegato niente. E questo significa una procedura speciale. Quindi sono andato al buio. Povere capre... Beh, non importa, troveremo una soluzione e puniremo i colpevoli. Sarà per loro istruzioni per le candele nigella sativa, tredicesimo stipendio in valuta forte... L'ha aumentato. Ho messo sul tavolo un quaderno con il profilo di Dante Alighieri in copertina e ho passato un po' di tempo concentrandomi sul foglio con la penna, e ho subito intuito che all'interno stava disegnando gli stessi profili di Dante, solo piccoli. Per qualche ragione queste persone pensano che durante il lungo ventesimo secolo Istruzioni per le candele Nigella Sativa non hanno studiato i loro metodi di lavoro. Mettere giù il blocco note. Si avvicinò a me, come se volesse guarire tutte le mie ferite emotive con il suo tardivo abbraccio, ma poi squillò il telefono sulla sua scrivania. Shmyga imprecò e prese il telefono. Ascoltò per alcuni secondi, poi il suo viso divenne cupo e attento. Si signore, . Ha detto e ha riattaccato. Guardandomi, alzò le mani con aria colpevole. Guarda cosa sta succedendo.

    Segreto commerciale. I marinai non hanno domande”, ha risposto Malyuta. Stepa lo guardò in modo strano. Esattamente tre ore dopo telefonò il capitano Lebedkin. Perché hai bisogno della mia vita? Salvato? - chiese minaccioso. - Così ti impegnerai nella politica. Io... - cominciò Stepa. "Non pisciare", disse allegramente il capitano. - Prendere in giro.

    Desiderio. Allora vai avanti, . disse Chapaev alzandosi da dietro albergo dell'estasi. Scesi dal vagone del quartier generale, siamo andati in fondo al treno. Ciò che stava accadendo mi sembrava sempre più strano. Molte delle carrozze che abbiamo incrociato erano buie e sembrava che lo fossero. Vuoto. Non c'era luce da nessuna parte; Non si sentiva un solo rumore da dietro le porte. Non potevo crederci dietro i pannelli di noce, sulla cui superficie lucida si rifletteva la luce del sigaro di Chapaev. La soldatessa rossa dorme, ma ho cercato di non pensarci.

    Entro il 2003, specialisti giapponesi. È stato possibile sviluppare un set di diverse microsonde vape idra direttamente il cervello e ha permesso, in una certa misura, di oggettivare l'immagine della percezione umana. L'attrezzatura giapponese non è stata in grado di determinare cosa provasse e pensasse esattamente la persona osservata. Ma ha permesso di ottenere un'immagine a colori (anche se sfocata) di ciò che ha vape idra. E non solo nella realtà, ma anche nella rapida fase del sonno. Ciò è stato possibile perché il segnale non è stato prelevato dal nervo ottico, ma da quelle zone. Cervelli responsabili della rappresentazione diretta. L'attrezzatura è stata immediatamente acquistata dalla squadra di Potashinsky. Il segnale proveniente da una serie di sonde impiantate nel cervello potrebbe essere trasmesso in modalità wireless. Connessioni che hanno permesso al bablonauta di condurre una vita normale, non vincolata in alcun modo dalla partecipazione all'esperimento. Era solo necessario che un ricevitore di segnale fosse posizionato da qualche parte nelle vicinanze. Che poi trasmetteva le informazioni al computer in tempo reale. In breve, lo schema degli esperimenti di Potashinsky assomigliava a questo: Innanzitutto, una serie di elettrodi di controllo è stata impiantata nel cervello dello sperimentatore bablonauta (i volontari, come al solito, sono stati selezionati tra i giovani ufficiali dell'FSB per questo ruolo).

    Da un punto morto. Ascolta, fratello", disse, "che razza di natura è questa?" Di cosa stai parlando? - chiese Isa. Beh, in macchina hai detto che il corpo frammentato da schegge ha la stessa natura di quello arcobaleno. E che razza di natura è questa? Faresti meglio a non chiedere informazioni su questo. Fratello", Isa si accigliò. Perché. Non sei ancora pronto per questo. Come non essere pronto. UN marijuana anfetaminica allo stesso tempo. Se fossi pronto, non lo chiederei. Quindi puoi rispondere. O.

    Presto. chiesto. Acquista hashish a Vladivostok adesso, ho detto, ecco... Qualcun altro è pignolo. Un amante potrebbe offendersi se non gli è permesso oltre il corridoio della rete. Ma Porfiry non è così. La prima cosa che ho fatto è stata connettermi ai suoi occhiali ogment. Bene, ha detto. Che basette... Intanto metto la foto degli occhiali sul pannello. Trasformandolo con una vista dalla telecamera sul soffitto. Ifak ​​ha sollevato qualsiasi morph senza mettere a dura prova il suo potere. Era mostruoso. Adesso Mara mi vedeva con i suoi occhiali ogment al posto dell'aifak e allo stesso tempo poteva osservare.

    Un codice sviluppato. A volte notava di essere nuovamente tormentato da sogni ricorrenti secondo lo schema 1. O secondo lo schema 2. E all'improvviso, in testo aperto, come un grido di fuga: Sognato fumare miscele 24 ore su 24, ucciso da me durante l'infanzia... Una voce dietro lo schermo. Tacque. Cosa sta facendo? - chiese Sam. "Mi sono addormentata", rispose Natasha. Sam le accarezzò delicatamente la punta pungente dell'addome e si appoggiò allo schienale del divano. Natasha deglutì piano. Sam tirò verso di sé la custodia dal pavimento, la aprì e tirò fuori una piccola teca di vetro. Il barattolo, ci ha sputato dentro del rosso, lo ha avvitato e gettato indietro: l'intera operazione lo ha preso fumare miscele tutto il giorno secondi "Lo sai, Natasha", disse.

    Dopodiché lui santo dell'hashish Ehi, Tatarskij. Nessuna risposta. Tatarsky aspettò un altro minuto e si rese conto di essere rimasto solo. Solo con la mente pronta a scatenarsi. Avevo urgente bisogno di occuparmi di qualcosa. Chiama, sussurrò. - A cui. Gireev. Lui sa cosa fare. Per molto tempo. Nessuno ha risposto al telefono. Alla fine, al quindicesimo o ventesimo squillo, Gireev rispose cupamente: Ciao. Andryusha.

    No, ha detto. C'è un uomo seduto in una stanza chiusa a chiave che non conosce il cinese. Dalla finestra gli danno appunti con domande in cinese. Per lui, questi sono solo pezzi di carta su cui sono disegnati degli scarabocchi, il cui significato non capisce. Ma la sua stanza è piena di libri diversi. Regole che descrivono in dettaglio come e in quale sequenza rispondere con semplici scarabocchi. E lui, agendo secondo queste regole, dà risposte in cinese in un'altra finestra. Creano completa fiducia in chiunque stia fuori che conosca il cinese. Anche se lui stesso non capisce affatto cosa gli chiedono. Indirizzo del browser Hydra Onion Qual è il significato delle sue risposte? Introdotto. Bene, l'ho presentato. Sura è la stessa stanza cinese, solo automatizzata. Invece di una persona con libri di consultazione, c'è uno scanner che legge i geroglifici. Un enorme database di riferimenti e regole che ti consentono di selezionare i geroglifici per la risposta.

    Ironicamente, questo è ciò che mi ha portato chiarezza. Almeno in termini pratici. Mi sono reso conto del problema che stavo affrontando. Non è solo complesso, è sfuggente. Era difficile anche formulare correttamente le domande ad esso legate. Sembrava l'unica consolazione Come trovare il sito Hydra nella lingua Torus, le cose sono altrettanto scivolose. Con la coscienza umana. Non sono stato in grado di affrontare questo problema. E ho deciso che la soluzione migliore per uscire dalla situazione sarebbe tornare. Riprendere le cose come al solito, lasciando gli esercizi esistenziali per dopo o dimenticandoli.

    Ben presto la strada condusse a un ricco villaggio con una chiesa bianca dipinta di fresco. Un soldato triste, con una gamba sola, in un'uniforme grigia sbiadita, sedeva vicino al recinto della chiesa. Non sai dove sia Optina Pustyn. chiese T. chinandosi verso di lui da cavallo. Questo è ciò di cui parlano i ragazzi. chiese il soldato. Che è stato recentemente istituito come stabilimento. Ho deciso che il militare era fuori di testa. Come è stato allestito di recente questo posto. Ciò significa che in ogni caso è tutto a posto, Vostro Onore, disse il soldato e Roulette dell'Idra mano, sarai ancora lontano. Ci sono solo due strade qui ed entrambe vanno in una direzione. Prendi la prima strada, oppure la seconda. Se vuoi un percorso più breve, allora attraverso la foresta. C'è un bivio lì, quindi puoi prendere entrambi i lati.

    E altre smorfie, di cui penso tu abbia sentito parlare molto... Lena non capiva che tipo di papà-mamma idiota diciottenne fosse (il giovane mormorò queste parole velocemente e a bassa voce), ma se ne dimenticò immediatamente - lei all'improvviso volle bere un sorso di vino per un valore di ventimila euro tale da farle venire l'acquolina in bocca. Un sospiro sommesso attraversò la sala, confermando che i presenti non avevano solo sentito parlare di smorfie. E siamo riusciti a studiare in dettaglio tutte le informazioni disponibili su di loro. "Recentemente, i servizi segreti occidentali hanno lanciato una vera caccia ai nostri ricchi idioti", ha continuato il giovane. - Hai sentito, ovviamente, dei grandi scandali indirizzo di hydra cipolla tk okey sito hydra in torus arresti: prima Courchevel, poi le Fiji, poi la boutique Hermes, e ora Saint Moritz, le Maldive e l'Antartide. La campagna è attentamente pianificata e ha due obiettivi principali: in primo luogo, screditare. Civiltà russa: stabilire il controllo sulle sue risorse raccogliendo prove compromettenti sui proprietari dei suoi beni principali. La nostra élite è diventata un bersaglio e la realtà oggettiva del punto attuale nello spazio-tempo è tale. Siamo diventati bersagli con lei. Aggrottando la fronte, tacque, come per dare ai suoi ascoltatori l'opportunità di rendersi conto della gravità della situazione. Poi il sorriso triste è tornato sul suo volto e ha continuato: Dobbiamo tenere la situazione sotto controllo.

    Lei sorrise. Almeno non devi fingere di essere offeso con innocenza di fronte alla tua stessa gente. Riguardo a cosa. Quando l'ho provocato. Quando saltò fuori nuda da Ingresso della cipolla di Idra e si fermò davanti a lui alla pecorina. La consideri una provocazione. Certamente. Perché, mi chiedo, gli hai voltato le spalle? Ho alzato le spalle. Per affidabilità. Cosa c'è di particolarmente affidabile in questo? La coda è più vicina al bersaglio”, dissi, non del tutto sicuro. BENE. E devi guardarti alle spalle.

    Terzo estasi montale alla vaniglia come segue: A Sua Eccellenza O. Konstantin Petrovich Pobedonostsev, funzionario. Con la presente trasmetto a Vostra Eccellenza la traduzione di un'antica iscrizione egiziana. Lamina d'oro rinvenuta in un medaglione estasi montale alla vaniglia il cadavere di padre Varsonofiy Netrebko nell'ambito delle indagini sul caso del conte T. Secondo gli specialisti del Museo Egizio, il contorno dei geroglifici ci consente di datare il testo all'era della XVIII dinastia o ad un periodo leggermente successivo. L'iscrizione recita: Il nome segreto dell'ermafrodita con la testa di gatto, che dà potere su di lui, è l'essenza. ANGC. Se riesci a controllare un ermafrodita con questo nome. Bene. Traduttori che ANGC possono anche essere tradotti come il tradizionale BHGV (o in altro modo, a seconda della scelta delle tabelle di corrispondenza quando si utilizzano i registri geroglifici). Il medaglione stesso, tuttavia, non può essere trasferito a Vostra Eccellenza nonostante la sua richiesta.

    Posta navigazione

  • I migliori articoli sull'argomento