簡體   English   中英

如何在 Laravel/Livewire 組件中持久化數據對象?

[英]How to persist data objects in Laravel/Livewire component?

我們有一個鏈接到視圖的 Livewire 組件,旨在顯示 data.table 超過 100,000 條記錄,每條記錄顯示許多(最多 300 條)子記錄,來自多種關系(主要是一對多和多對多). 在長時間的優化和調試工作之后,為了獲得 Eloquent 集合(以 15 個為一組分頁)的繁重查詢現在需要大約 15-20 秒才能完成。 該視圖還旨在通過激活模態 windows 允許 CRUD 操作。我們需要解決的主要問題是顯示添加或編輯模態 window 的大延遲。此時,我們不需要運行 data.table再次查詢,因為底層記錄還沒有改變。 主要查詢應該只在初始加載時運行,或者在記錄添加或更新之后運行,此時分頁的 data.table 可能已更改。

這是組件的摘錄,顯示了相關代碼:

public function render()
    {
        //******************** SECTION A *************** 
        //This is the eloquent collection we use to generate the data-table in the view
        //$q is a public property, binded to a input box, for searching purposes. $sortBy and $sortAsc are used to order the data-table.  
        
        $fonos = Fono::select('id','cod','titulo','sello_cod','ritmo_cod','propiedad_cod')
            ->with(['propiedad:id,cod,propiedad','sello:id,cod,sello','ritmo:id,cod,ritmo','artistas:id,cod,artista','autores:id,cod,autor','operadores:id,cod,operador','isrcs:id,isrc']) //Eager loading relationships to reduce queries (only required fields)
            ->when( $this->q, function($query) {
                return $query->where(function( $query) {
                    $query->where('titulo', 'like', '%'.$this->q . '%')
                        ->orWhere('cod', 'like', '%' . $this->q . '%')
                        ->orWhere('isrc', 'like', '%' . $this->q . '%')
                        ->orWhere('famoso_por', 'like', '%' . $this->q . '%')
                        ->orWhereHas('sello', function ( $query) {
                            $query->where('sello', 'like', '%' . $this->q . '%');})
                        ->orWhereHas('autores', function ( $query) {
                            $query->where('cod', 'like', '%' . $this->q . '%');}) 
                        ->orWhereHas('autores', function ( $query) {
                            $query->where('autor', 'like', '%' . $this->q . '%');})
                        ->orWhereHas('artistas', function ( $query) {
                            $query->where('cod', 'like', '%' . $this->q . '%');})            
                        ->orWhereHas('artistas', function ( $query) {
                            $query->where('artista', 'like', '%' . $this->q . '%');}) 
                        ->orWhereHas('operadores', function ( $query) {
                            $query->where('cod', 'like', '%' . $this->q . '%');})
                        ->orWhereHas('operadores', function ( $query) {
                            $query->where('operador', 'like', '%' . $this->q . '%');})
                        ->orWhereHas('isrcs', function ( $query) {
                            $query->where('isrc', 'like', '%' . $this->q . '%');})          
                        ;
                });
            })
            ->orderBy($this->sortBy, $this->sortAsc ? 'ASC' : 'DESC')->paginate(15);

        //******************** SECTION B *************** 
        //Here we load other Eloquent models (some with thousands of records), that are used to populate several selects (drop-down lists), that are visible when the modal window is activated to add or edit a record. 

        $tipos_fonos = TipoFono::select('cod','tipo_fono')->get();
        $sellos = Sello::select('cod','sello')->get();
        $ritmos = Ritmo::select('cod','ritmo')->get();
        $generos = Genero::select('cod','genero')->get();
        $idiomas = Idioma::select('id','idioma')->get();
        $propiedades = Propiedad::select('cod','propiedad')->get();
        $territorios = Territorio::select('cod','territorio')->get();
        $clases_artistas = ClaseArtista::select('id','clase_artista')->get();
        $artistasLista = Artista::select('cod','artista')->get();
        $papelesLista = PapelArtista::select('id','papel_artista')->get();
        $autoresLista = AutorFono::select('cod','autor')->get();
        $operadoresLista = Operador::select('cod','operador')->get();
        $tipos_autores = TipoAutor::select('id','cod','tipo_autor')->get();

        //******************** SECTION C ***************   
        //Finally, we pass the objects to the view

        return view('livewire.admin.fonos', [
            'fonos' => $fonos,
            'tipos_fonos' => $tipos_fonos,
            'sellos' => $sellos,
            'ritmos' => $ritmos,
            'generos' => $generos,
            'idiomas' => $idiomas,
            'propiedades' => $propiedades,
            'territorios' => $territorios,
            'clases_artistas' => $clases_artistas,
            'artistasLista'  => $artistasLista,
            'papelesLista'  => $papelesLista,
            'tipos_autores'  => $tipos_autores,
            'autoresLista'  => $autoresLista,
            'operadoresLista'  => $operadoresLista,
        ]);
    }

顯然,我們需要一種方法來避免不必要的查詢,但我們發現了實現該目標的重要限制。 例如:

  1. 我們試圖在初始加載時在 memory 中存儲用於填充 select 元素的 Eloquent 模型(集合),因為該數據在生命周期中不會更改。 因此,我們將這些查詢移至 mount 方法,例如:

     public function mount() { // This runs once, like a constructor class //Load all models for select drop-downs //all the variables are declared as public properties and also included in the $rules array (ie 'lw_generos.*.cod' => 'nullable') $this->lw_tipos_fonos = TipoFono::select('cod','tipo_fono')->get(); $this->lw_sellos = Sello::select('cod','sello')->get(); $this->lw_ritmos = Ritmo::select('cod','ritmo')->get(); $this->lw_generos = Genero::select('cod','genero')->get(); $this->lw_idiomas = Idioma::select('id','idioma')->get(); $this->lw_propiedades = Propiedad::select('cod','propiedad')->get(); $this->lw_territorios = Territorio::select('cod','territorio')->get(); $this->lw_clases_artistas = ClaseArtista::select('id','clase_artista')->get(); $this->lw_artistasLista = Artista::select('cod','artista')->get(); $this->lw_papelesLista = PapelArtista::select('id','papel_artista')->get(); $this->lw_autoresLista = AutorFono::select('cod','autor')->get(); $this->lw_operadoresLista = Operador::select('cod','operador')->get(); $this->lw_tipos_autores = TipoAutor::select('id','cod','tipo_autor')->get(); }

    所以,為了與這個修改保持一致,我們更改了 render 方法的 C 部分:

     return view('livewire.admin.fonos', [ 'fonos' => $fonos, 'tipos_fonos' => $this->lw_tipos_fonos, 'sellos' => $this->lw_sellos, 'ritmos' => $this->lw_ritmos, 'generos' => $this->lw_generos, 'idiomas' => $this->lw_idiomas, 'propiedades' => $this->lw_propiedades, 'territorios' => $this->lw_territorios, 'clases_artistas' => $this->lw_clases_artistas, 'artistasLista' => $this->lw_artistasLista, 'papelesLista' => $this->lw_papelesLista, 'autoresLista' => $this->lw_autoresLista, 'operadoresLista' => $this->lw_operadoresLista, 'tipos_autores' => $this->lw_tipos_autores, ]);

    這種方法的問題是它會產生一個錯誤:

    Symfony\Component\ErrorHandler\Error\FatalError 最大執行時間超過 30 秒 Symfony\Component\ErrorHandler\Error\FatalError::__construct vendor/laravel/framework/src/Illuminate/Collections/Arr.php:115

    注意:我們嘗試直接在視圖中使用 Eloquent collections 的公共變量,而不顯式傳遞它們,但結果是一樣的。 我們還考慮過將屬性聲明為私有或受保護的,但數據不會持久化(並且 Livewire 文檔指出私有或受保護的屬性不會在組件更新后繼續存在)。

  2. 我們正在努力避免在視圖渲染期間發生的不必要的查詢。 $fonos Eloquent 集合應僅在數據更改(以模態形式完成的修改)或使用搜索輸入時更新。 在其他情況下,集合應作為 object 存儲在 memory 中,准備傳遞給視圖。 但實現該目標存在障礙:首先,分頁為 collections 的 eloquent 目前無法存儲為 Livewire 屬性(僅允許 JavaScript 兼容數據)。 其次,即使我們可以將集合分配給公共屬性,我們也會遇到與前一點所述相同的錯誤。

我們正在使用 Laravel 8.28.1 和 Livewire 2.3.18。

我們通過使用 Livewire 計算屬性和緩存查詢結果解決了這個問題。 例如:

public function getBancosProperty(){
        /* Caching */
        $expire = Carbon::now()->addMinutes(10);
        $bancos = Cache::remember('bancos', $expire, function() {
            return Banco::select('cod','banco')->orderBy('banco')->get();
        });
        return $bancos;
}

希望這對其他人有幫助。

問候。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM