Laravel Nova
訂單系統是今年改版的一個項目,洽逢 Laravel Nova 釋出一段時間,我們已取得授權,便決定用 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;
// ...
}