Laravel Nova

訂單系統是今年改版的一個項目,洽逢 Laravel Nova 釋出一段時間,我們已取得授權,便決定用 Nova 開發查詢後台。

詳細的安裝步驟官網寫得很清楚,可直接參考官網:

Laravel Nova 安裝方法看這

自訂登入路由

安裝完 Nova 後,接著要開始客製化——首先是登入方式。由於公司用的是 G suite 帳號,因此希望可以直接綁定 Google 帳號登入。

這邊我們要去覆寫 Nova 註冊 routes 的地方。首先在 app\Foundation\Nova 下建立 PendingRouteRegistration,擴展原本的 Laravel\Nova\PendingRouteRegistration,接著覆寫 withAuthenticationRoutes(),將 Route 改成自定義的路徑。

namespace App\Foundation\Nova;

// ...
use Laravel\Nova\PendingRouteRegistration as LaravelPendingRouteRegistration;

class PendingRouteRegistration extends LaravelPendingRouteRegistration
{
    // ...
    public function withAuthenticationRoutes($middleware = ['web'])
    {
        Route::namespace('App\Http\Controllers')
            ->domain(config('nova.domain', null))
            ->middleware($middleware)
            ->as('nova.')
            ->prefix(Nova::path())
            ->group(function () {
                Route::get('/login', 'Auth\LoginController@showLoginForm');
                Route::post('/login', 'Auth\LoginController@login')->name('login');
            });

        Route::namespace('App\Http\Controllers')
            ->domain(config('nova.domain', null))
            ->middleware(config('nova.middleware', []))
            ->as('nova.')
            ->prefix(Nova::path())
            ->group(function () {
                Route::get('/logout', 'Auth\LoginController@logout')->name('logout');
            });

        return $this;
    }
}

接著用相同方式,在同個命名空間下新建 Nova 去擴展原本的 Laravel\Nova\Nova,並覆寫原本的 routes(),改為回傳我們剛才自建的 PendingRouteRegistration

namespace App\Foundation\Nova;

use App\Foundation\Nova\PendingRouteRegistration as AppPendingRouteRegistration;
// ...
use Laravel\Nova\Nova as LaravelNova;

class Nova extends LaravelNova
{
    // ...
    public static function routes()
    {
        Route::aliasMiddleware('nova.guest', RedirectIfAuthenticated::class);

        return new AppPendingRouteRegistration;
    }
}

最後,在 app\Providers\NovaServiceProvider 中將原本使用的 Laravel\Nova\Nova 改成我們的 App\Foundation\Nova\Nova,就可以註冊我們自定義的路由了。

namespace App\Providers;

use App\Foundation\Nova\Nova;
// ...

class NovaServiceProvider extends NovaApplicationServiceProvider
{
    // ...
    protected function routes()
    {
        Nova::routes()
                ->withAuthenticationRoutes()
                ->withPasswordResetRoutes()
                ->register();
    }
    // ...
}

跨資源搜尋

Laravel Nova 的搜尋功能只能針對單一資源搜尋,無法跨資源搜尋,例如今天 users (使用者)posts (貼文)hasMany 的關係,我無法透過貼文的 title 去搜尋到使用者,而只能先搜尋到貼文,再從貼文的頁面連結到使用者。

為解決這個問題,我們要在 Nova 的搜尋功能加入 Relation 搜尋。首先我們找到 Laravel\Nova\Resource,發現它有使用 PerformsQueries 這個 trait,並發現 applySearch() 這個方法,所有搜尋的 query 都會進到這裡,實際的搜尋也在這裡執行。

原先我想仿照上面做法,抽換整個 Laravel\Nova\Resource 後再覆寫這個方法,但在聽了同事建議後,改為在 App\Foundation\Nova 新建一個 RelationSearchable,從這邊覆寫 applySearch()

namespace App\Foundation\Nova;

trait RelationSearchable
{
    protected static function applySearch($query, $search)
    {
        return $query->where(function ($query) use ($search) {
            
            // ...

            // 上面維持原本,下面加入 Relation 的搜尋
            foreach (static::$with as $relation) {
                $query->orWhereHas($relation, function ($query) use ($search, $likeOperator) {
                    $model = $query->getModel();
                    $resource = \Laravel\Nova\Nova::newResourceFromModel($model);
                    $query->where(function ($query) use ($search, $model, $resource, $likeOperator) {
                        foreach ($resource::searchableColumns() as $column) {
                            $query->orWhere($model->qualifyColumn($column), $likeOperator, '%'.$search.'%');
                        }
                    });
                });
            }
        });
    }
}

最後只要在有需要這個功能的資源使用這個 trait 便行,而不需要所有資源都使用它。

namespace App\Nova;

use App\Foundation\Nova\RelationSearchable;
// ...

class Order extends Resource
{
    use RelationSearchable;

    // ...
}
Last Updated: 7/28/2019, 2:09:38 PM