1. 程式人生 > >雲客Drupal8原始碼分析之配置實體查詢

雲客Drupal8原始碼分析之配置實體查詢

本系列已經發布過內容實體的實體查詢,配置實體也是有實體查詢的,比如:塊知識庫服務(\Drupal\block\BlockRepository)中就用到配置實體查詢,以查出相同主題下的塊配置實體。配置實體的資料通過配置系統儲存,先回顧一下我們獲取一個配置值的方式:

        $config = \Drupal::configFactory()->get('system.performance');
        $config->get('cache.page.max_age');

在獲取配置物件後,我們是依據點號分隔的配置路徑表明我們需要的配置值的,在獲取值時,這個路徑就像內容實體的欄位一樣,drupal提供的配置實體查詢非常強大,只需要提供路徑、期望的值、對值的操作符作為條件,然後通過and或or連結詞將條件連線起來就可以依據配置中的任意值進行查詢或統計了。

預設查詢工廠:
和內容實體一樣,配置實體也有一個通用查詢工廠服務,她為所有的配置實體型別例項化出查詢物件,後者是我們操作查詢的互動物件(見後),配置查詢工廠服務定義如下:
服務id:entity.query.config
類:Drupal\Core\Config\Entity\Query\QueryFactory
獲取方法:\Drupal::service('entity.query.config');
通過她的get方法可以獲取要使用的查詢物件:
\Drupal::service(‘entity.query.config’)->get($entityTypeID, $conjunction);
獲取查詢物件更通用的方法(和內容實體有相同的形式):
\Drupal::entityQuery($entityTypeID, $conjunction = 'AND');
查詢物件使用示例:

        $entity_query =\Drupal::entityQuery('block');
        $entity_query->accessCheck(FALSE);
        $entity_query->condition('theme', 'bartik');
        $result = $entity_query->execute();
        print_r($result);
        die;

以上程式碼將返回bartik主題下的所有塊配置實體id。

以上工廠服務是配置實體查詢的通用工廠,配置實體在實體儲存處理器的getQueryServiceName方法中可以指定自定義工廠。

原理:
我們知道內容實體是分欄位存放資料庫表的,通過構建SQL語句執行查詢,而配置實體的資料是存放在配置系統中,在配置系統中資料是序列化後儲存在一個欄位中的,這就不能再使用sql查詢了,只能載入全部配置實體對應的配置物件,然後逐一解析篩選,原本由資料庫進行的工作現在由php完成,這樣的工作量是很大的,有沒有什麼優化方法呢?
首先分析一下配置資料的特性,在很多使用場景下,內容資料的任意欄位都可能成為搜尋依據,而配置資料不太一樣,往往只有一個或幾個值會成為搜尋依據,其次配置資料在數量上是比較少的,根據這些特性,drupal的做法是為配置實體專門建立用於搜尋優化的資料,對可預見的搜尋配置項進行特別處理:
在定義配置實體時,我們在實體類釋文中以lookup_keys根鍵指出要做搜尋優化的配置項路徑,可以有多個,這稱為定義查詢鍵,路徑由點號分隔的配置鍵名組成,和從配置物件獲取值時指定的路徑一樣,但不一樣的是可以使用萬用字元“*”匹配某層級的所有鍵名,開始層級和最後層級不能使用萬用字元,如:
tour實體的lookup_keys 的值為:"routes.*.route_name",
塊實體的為:"theme"
沒有被定義為查詢鍵的配置路徑不做搜尋優化(但同樣可以搜尋),系統在所有的配置實體中都預設追加了uuid作為配置查詢鍵,因此通過uuid查詢是經過優化的,速度會很快。

系統使用鍵值儲存系統(見本系列鍵值儲存主題)來儲存配置實體的搜尋優化資料,對應的資料庫表為:key_value,以“config.entity.key_store.實體型別id”作為鍵值儲存集,以此例項化鍵值儲存物件,搜尋優化資料的結構如下:

搜尋鍵:
儲存在資料庫表的name欄位,字串值,格式如下:
“值路徑:配置值”
其中值路徑就是上文講到的實體釋文根鍵lookup_keys指定的配置值路徑(沒有替換萬用字元),配置值必須是標量資料,如果不是標量資料,那麼需要修改值路徑以進一步精確指定,配置值是經過配置覆寫後的值;每行儲存一個配置值。
配置名陣列:
儲存在資料庫表的value欄位,以序列化方式儲存,值是一個由配置名構成的陣列,可通過這些配置名得出配置實體的id,這些配置實體在對應的配置路徑上的配置值是一樣的。

有了這樣的資料結構,在進行lookup_keys 中定義路徑的“=”操作查詢時,就大大提高了搜尋速度,從而起到搜尋優化的作用,如果是進行其他搜尋操作,那麼還得老老實實載入配置物件,然後具體解析,該工作由查詢物件來完成,好在系統中絕大部分查詢都是“=”操作,因此很有必要通過設定lookup_keys來提高查詢速度。

以上這些搜尋優化資料是什麼時候儲存的呢?因為配置查詢服務實現了訂閱器,她訂閱了配置的儲存和刪除事件,以此維護這些資料。

查詢物件:
提供完整的搜尋功能,取得配置查詢物件可以使用以下語句:
$entity_query =\Drupal::entityQuery($entityTypeID, $conjunction = 'AND');
或者在明確知道是配置實體時使用以下語句:
$entity_query =\Drupal::service(‘entity.query.config’)->get($entityTypeID, $conjunction);
我們可以在上面設定查詢條件,以得到查詢結果,預設的配置查詢物件是:
\Drupal\Core\Config\Entity\Query\Query
該類設計的非常精妙,這裡做一些說明幫助理解:
\Drupal\Core\Config\Entity\Query\Query::loadRecords:
為減小搜尋範圍,該方法將返回一個通過上文搜尋優化資料計算出的配置記錄,不管是and還是or條件,最終的查詢結果一定在該範圍內,返回值是一個數組,鍵名是配置實體id,值為配置資料(陣列形式,$config->get()返回的值),返回的這個範圍陣列會送入條件物件中運用其他條件,進一步減小範圍,直到最終的搜尋結果
\Drupal\Core\Config\Entity\Query\Condition::compile:
查詢物件將使用該條件物件,編譯方法將計算所有的條件,最終確定搜尋結果

查詢物件方法:

$entity_query =\Drupal::entityQuery($entityTypeID);
$entity_query ->condition($property, $value = NULL, $operator = NULL, $langcode = NULL):
引數解釋如下:
條件屬性$property:
稱為路徑,是指配置值所在的路徑,以點號分隔,就像在配置物件上獲取值時所用路徑是一樣的,只不過這裡允許用萬用字元“*”代表路徑中某層級的所有鍵名,這提供了強大的能力,在路徑帶有“*”的情況下,只要被其匹配的項有一個滿足條件,結果就滿足條件,需要注意的是這裡和前文講到的設定lookup_keys不同,“*”可以在路徑的如何層級,包括開始和結束。
該引數也可以是條件物件,詳見條件組

條件值:
配置實體查詢中條件值必須是標量資料(如果操作符是IN、NOT IN則可以是標量資料組成的陣列),值是大小寫不敏感的,在內部將全部轉換為小寫,配置實體查詢不支援針對大小寫的查詢,建議小寫。

操作符:
操作符是大小寫敏感的,必須大寫,條件連線詞(AND,OR)大小寫不敏感;在預設配置實體查詢物件上只能使用以下操作符(其他操作符將丟擲無效操作符異常):
大於:>
小於:<
等於:=, (可省略,預設操作符)
大於等於:>=
小於等於:<=
不等於:<> (注意不可以使用!=)
不在其中:NOT IN (提供的值為陣列)
在其中: IN (提供的值為陣列,可省略,預設操作符)
以值字串開始:STARTS_WITH
以值字串結束:ENDS_WITH
包含值字串:CONTAINS
判斷是否為空:'IS NULL', 'IS NOT NULL' (等同於isset($value),空字串不是NULL)

exists($property, $langcode = NULL)
notExists($property, $langcode = NULL)

判斷配置是否存在,相當於使用條件方法時將值設為NULL,再使用操作符'IS NULL', 'IS NOT NULL'

range($start = NULL, $length = NULL):
限定查詢範圍

sort($field, $direction = 'ASC', $langcode = NULL)
排序,比如按權重排序

count():
設定查詢為計數,呼叫後將返回查詢所得的配置總數

execute():
返回內容是一個實體id構成的陣列,鍵名和鍵值相同,都是實體id,如果呼叫過計數方法將返回一個整數。

注意:預設配置查詢物件上的許多方法來自查詢基類,他們在配置查詢中用不到,呼叫是無意義的,如:訪問檢查、聚合方法等,他們是為內容實體查詢準備的。

示例程式碼:
找出bartik主題下第一側邊欄中排序值小於-5的塊:

        $entity_query = \Drupal::entityQuery('block', 'and');
        $entity_query->condition('theme', 'bartik');
        $entity_query->condition('region', 'sidebar_first');
        $entity_query->condition('weight', -5, '<');
        $result = $entity_query->execute();
        $entities = \Drupal::entityTypeManager()->getStorage("block")->loadMultiple($result);
        print_r($entities);
        die;

找出存在角色條件限制的所有區塊:

        $entity_query = \Drupal::entityQuery('block', 'and');
        $entity_query->exists('visibility.user_role');
        //$entity_query->exists('visibility.*'); //這樣將找出有任意條件限制的區塊
        $result = $entity_query->execute();
        $entities = \Drupal::entityTypeManager()->getStorage("block")->loadMultiple($result);
        print_r($entities);
        die;

找出區塊配置中標題以“自定義”三個字開頭的區塊:

        $entity_query = \Drupal::entityQuery('block', 'and');
        $entity_query->condition('settings.label', '自定義','STARTS_WITH');
        $result = $entity_query->execute();
        $entities = \Drupal::entityTypeManager()->getStorage("block")->loadMultiple($result);
        print_r($entities);
        die;

查詢某主題下所有區塊的數量:

        $entity_query = \Drupal::entityQuery('block', 'and');
        $entity_query->condition('theme', 'bartik');
        $entity_query->count();
        echo $num = $entity_query->execute();
        die;

查詢某主題下特定id的塊:

        $entity_query = \Drupal::entityQuery('block', 'and');
        $entity_query->condition('theme', 'bartik');
        $entity_query->condition('*.id', 'system_messages_block');
        $result = $entity_query->execute();
        $entities = \Drupal::entityTypeManager()->getStorage("block")->loadMultiple($result);
        print_r($entities);
        die;

設定條件組:
有時我們需要進行復雜的查詢,將and和or混用,這就設計到如何將條件分組了,這和內容實體查詢一樣,這裡提供一個程式碼,查詢出bartik或者seven下啟用的且具備可視條件設定的塊實體:

        $query = \Drupal::entityQuery('block', "AND");
        $or_group = $query->orConditionGroup()
            ->condition('theme', 'bartik')
            ->condition('theme', 'seven');
        $query->condition($or_group);
        $and_group = $query->andConditionGroup()
            ->condition('status', '1')
            ->condition('visibility.*', NULL, 'IS NOT NULL');
        $query->condition($and_group);
        $result = $query->execute();
        $entities = \Drupal::entityTypeManager()->getStorage("block")->loadMultiple($result);
        print_r($entities);
        die;

該示例分了兩個組,然後將他們以and連線,相當於:

((a || b)&&(c && d))

其他實體查詢:
除內容實體查詢、配置實體查詢外,系統還提供了一些其他型別的實體查詢:
鍵值對實體查詢:
用於使用鍵值對儲存系統來儲存資料的實體的查詢:
服務id:entity.query.keyvalue

空實體查詢:
不進行任何有意義的查詢,一些不需要進行實體查詢的實體型別用她來保持程式形式上的統一,從而在呼叫時不發生異常:
服務id:entity.query.null

補充:
1、注意配置沒有更新事件,儲存和刪除足以建立搜尋優化資料
2、配置實體不支援聚合Aggregate查詢,這沒有意義
3、在配置實體有配置覆寫的情況下需要注意:在配置實體查詢的查詢優化資料中(也就是鍵值儲存系統中)儲存優化資料是使用的帶覆寫的值,而刪除時使用不帶覆寫的值,這在一些情況下可能導致錯誤或資料冗餘

我是雲客,【雲遊天下,做客四方】,微訊號:php-world,歡迎轉載,但須註明出處,討論請加qq群203286137

相關推薦

Drupal8原始碼分析配置實體查詢

本系列已經發布過內容實體的實體查詢,配置實體也是有實體查詢的,比如:塊知識庫服務(\Drupal\block\BlockRepository)中就用到配置實體查詢,以查出相同主題下的塊配置實體。配置實體的資料通過配置系統儲存,先回顧一下我們獲取一個配置值的方式:

Drupal8原始碼分析內容實體資料庫表結構及表對映table mapping

“欄位”概念 在drupal中提到“欄位”這個概念時,請不要理解為資料庫中表的一個列,這不是一個概念,它是指一個欄位物件,充當著實體物件的屬性,也是一個列表型別的型別化資料物件,當本系列提到“欄位field”一般均是指欄位物件或欄位定義物件,而資料庫表中的欄位列,則將其稱為

Drupal8原始碼分析實體Entity(二)配置實體基類

配置實體基類是系統定義的一個用於配置實體的抽象基類,繼承自實體基類,完成了配置實體的大部分通用功能,具體的配置實體往往會繼承它,比如使用者角色實體,這樣寫少量程式碼即可,類定義如下: Drupal\Core\Config\Entity\ConfigEntityBase 實

Drupal8原始碼分析實體Entity(五)內容實體基類

原始碼分析重點在於在自己的大腦中重現開發者的思維過程,內容實體基類是drupal中很大的一個類,她要處理眾多的問題,內容實體的大多數功能都集中在這裡,開發者有許多的考慮,要弄清楚她的所有細節,學習者可能會覺得有些困難,這時需要明白任何複雜龐大的事物都是一步步累積發展起來的,

Drupal8原始碼分析實體查詢entityQuery

通過本系列前面內容的學習你已經知道實體在資料庫中是如何儲存的,簡單來說儲存實體的資料庫表分為兩大類,專用表和共享表,共享表必有基本表,可能有版本表、資料表、版本資料表,總之大多數情況下一個完整的實體被儲存在多張表中,比如我們在後臺建立一個內容型別,她的資料至少儲存在六張表中

Drupal8原始碼分析外掛系統(下)

以下內容僅是一個預覽,完整內容請見文尾: 至此本系列對外掛的介紹全部完成,涵蓋了系統外掛的所有知識 全文目錄(全文10476字): 例項化外掛 外掛對映Plugin mapping 外掛上下文  

Drupal8原始碼分析臨時儲存與訊息服務

前言:臨時儲存與訊息服務之間並沒有什麼直接關聯,由於她們都是系統基礎元件,內容也比較簡單,為後續主題做準備,所以放在一起講解。 臨時儲存概述: 臨時儲存用來儲存一些臨時性的資料,超期後會被刪除,比如節點在儲存前的預覽資料,她和快取不一樣,她是臨時性的、不能被重建的資料,依據

Drupal8原始碼分析塊系統block

在drupal中系統流程指向一個控制器,通常控制器返回一個代表特定內容的渲染陣列,那麼還需要其他內容怎麼辦?這就是塊系統要解決的,她讓頁面精彩紛呈,可展示多種資訊或工具,如果沒有她頁面會非常單調,某種程度上說她是系統必須的,給各模組展示資訊提供頁面視窗。 從控制器返回的渲染

Drupal8原始碼分析前言

Drupal是一個非常優秀的網站系統,可以說她是一個網站應用開發框架,也可以說是一個cms,她在世界範圍內被廣泛使用,最為人所知的是美國白宮、聯合國等知名機構的官方網站使用了她,隨著Drupal8的來到,她又達到了一個全新的高度,全面的由面向過程開發轉為面向物件開發,程式碼

Drupal8原始碼分析語言Language

據聯合國教科文組織曾釋出的《瀕危語言圖譜》的資料,在這顆星球上我們的文明有7000種語言,另據德國出版的《語言學及語言交際工具問題手冊》有5651種語言,具體有多少誰也說不清,但我們知道人類文明大體有數千種語言之多,中國就有129種語言,世界80%的人講83種主要語言,dr

Drupal8原始碼分析外掛系統(上)

各位《雲客drupal8原始碼分析》系列的讀者: 本系列一直以每週一篇的速度進行部落格原創更新,希望幫助大家理解drupal8底層原理,並縮短學習時間,但自《外掛系統(上)》主題開始部落格僅釋出前言和目錄,這是因為雲客在思考一個問題:drupal在國外如此流行但在國內卻很小

Drupal8原始碼分析主題鉤子註冊theme.registry

以下內容僅是一個預覽,完整內容請見文尾:全文目錄(全文11509字):主題鉤子定義:主題註冊服務:主題實現:主題鉤子註冊:主題鉤子型別:函式註冊:掃描註冊:註冊順序及覆寫:主題鉤子、主題函式、模板命名:

Drupal8原始碼分析表單Form API

在閱讀本主題前建議你先閱讀本系列前面的《表單定義示例》主題,看一看在drupal8中是如何運用表單的。表單處理流程:一般情況下表單流程是先顯示一個表單,使用者填寫,然後提交,系統處理,如果有錯則重新顯示並給出錯誤提示,反之沒有錯誤那麼完成後給出一個響應或者一個重定向響應,這是

Drupal8原始碼分析資料驗證Validation

各位《雲客drupal8原始碼分析》系列的讀者: 本系列一直以每週一篇的速度進行部落格原創更新,希望幫助大家理解drupal8底層原理,並縮短學習時間,但自《外掛系統(上)》主題開始部落格僅釋出前言和目錄,這是因為雲客在思考一個問題:drupal在國外如此流行但在國內卻很小

Drupal8原始碼分析主題協商theme negotiator

drupal主題系統十分靈活,你可以全站使用一套主題,用響應式設計去相容移動端和pc端,如果響應式無法滿足要求,你可以在各端分別使用不同的主題,但其靈活性遠不止如此,實際上在同一個站點中你可以根據任意條件使用不同的主題,系統後臺設定的只是預設值而已,比如在pc端或移動端你可以

Drupal8原始碼分析響應附屬處理attachments_processor

在閱讀本主題前,你需要先閱讀本系列的渲染陣列、渲染器、渲染佔位符等主題 附屬物attachments就是渲染陣列的#attached部分,這裡稱為“附屬物”而不叫做“附件”,以便和圖片、檔案等概念相區別,附屬物有如下8個型別(以在#attached中的鍵名列出,如果添加了其

Drupal8原始碼分析資料庫系統及其使用

在開始本主題前請允許一點點題外話: 在我寫這個部落格的時候(2016年10月28日),《Begining Drupal 8》這本書已經翻譯完成並做成了PDF格式供給大家免費下載,這是一本引導新人學習drupal8的入門級教程,由drupal中文社群站http://drupa

Drupal8原始碼分析快取系統Cache

在介紹drupal8的快取系統前我們先了解一下快取系統的本質及特性,快取的存在依賴於兩個目的:節省資源和提高速度,起不到這兩作用則快取沒有存在的必要,當一個結果需要進行大量計算才能得到,而它又不會頻繁

Drupal8原始碼分析PHP程式碼儲存PhpStorage

在做專案時,有時需要儲存php程式碼,由於她是可執行的,我們並不希望被隨意執行或者修改,drupal提供了一個php程式碼儲存元件來保障這一點,她使用檔案系統儲存,本篇講解她的使用和原理。 前備知識點: 首先我們需要明確知道檔案系統操作的以下幾點: 一個檔案有三個時間:

Drupal8原始碼分析快取上下文CacheContext

“上下文Context”這個詞是什麼意思呢?平常生活中它常見於語言、文字交流裡面,意思是當前交流處於一個特定的環境下,依託前面的內容交流才有意義 比如這句話:“他正在學習drupal”,如果單獨說是沒