# Konfiguracja modeli
# Filtrowanie
Uwaga
Model musi implementować trait Modules\Core\Traits\Filterable
# Rodzaje filtrów
Filtrowanie odbywa się poprzez wysłanie zapytania z odpowiednimi parametrami/atrybutami.
Dostępne rodzaje filtrów:
- domyślny - przeszukiwany atrybut musi zawierać przeszukiwaną frazę (np.
foo=bar) - wartość minimalna - należy wysłać atrybut jako tablicę (np.
foo[min]=10) - wartość maksymalna (np.
foo[max]=10) - dokładna wartość atrybutu (np.
foo[strict]=bar) - wartość różna od przesłanej (np.
foo[not]=bar) - przypadku przesłania listy wartości (tablicy) przeszukiwany atrybut musi zawierać przynajmniej jeden element (np.
foo[]=bar&foo[]=baz)
# Filtrowanie po atrybutach modelu
Core domyślnie pozwala na filtrowanie modelu po jego wszystkich kolumnach w bazie danych.
Uwaga
Lista kolumn tabel jest wczytywana z cache, aby ograniczyć ilość zapytań SQL.
Jeśli pola w tabeli zmienią się, należy skasować cache, korzystając z komendy Laravela php artisan cache:clear
Możemy zawęzić listę filtrowanych pól dodając do modelu własność $filterable, która powinna zwierać listę atrybutów pól do filtrowania.
protected $filterable = ['first_name', 'last_name', 'email'];
TIP
Atrybut $filterable może zawierać pola wielojęzyczne
# Filtrowanie po atrybutach relacji
Dodatkowo Core pozwala na filtrowanie danych po atrybutach relacji.
W takim przypadku należy dodać relację do modelu.
public function profile()
{
return $this->hasOne(Profile::class);
}
Następnie skorzystać z własności $relationFilterable.
protected $relationFilterable = [
'profile_name' => [ // nazwa pola, używana do filtrowania
'relation' => 'profile', // nazwa relacji
'attribute' => 'name', // atrybut modelu relacji
],
];
# Filtrowanie po atrybutach niestandardowych
W modelu należy dodać metodę customFilters, która powinna zwrócić tablicę filtrów:
use Illuminate\Database\Eloquent\Builder;
protected function customFilters(): array
{
return [
// class based filter
'price_over' => PriveOverFilter::class,
// or anonymous function
'price_under' => fn (Builder $builder, float $value) => $builder->where('price', '<', $value),
];
}
Filtr na bazie klasy powinien zawierać metodę apply, w której należy zdefiniować logikę filtra.
use Illuminate\Database\Eloquent\Builder;
class PriceOverFilter
{
public function apply(Builder $query, float $value): Builder
{
return $query->where('price', '>', $value);
}
}
# Wyszukiwanie
Uwaga
Model musi implementować trait Modules\Core\Traits\Searchable
Domyślnie wyszukiwanie odbywa się przekazując parametr query search.
Nazwę tego parametru można zmienić globalnie dla całej aplikacji, ustawiając default-searchable-query-param w pliku Modules\Core\Config\config.php,
lub tylko dla konkretnego modelu korzystając z własności protected $searchableQueryParam.
Wyszukiwana fraza dzielona jest na części, na podstawie wyrażenia regularnego \s+, a następnie wyszukiwana jest każda z części, w którymkolwiek ze zdefiniowanych atrybutów. Jeśli jakaś część nie zostanie dopasowana - model nie zostanie zwrócony.
# Wyszukiwanie po polach modelu
protected $searchable = ['first_name', 'last_name'];
# Wyszukiwanie po polach relacji
public function profile()
{
return $this->hasOne(Profile::class);
}
protected $customSearchable = [
'profile' => [ // relacja
'name', 'phone', 'email', // atrybuty modelu relacji
],
];
TIP
Wyszukiwanie obejmuje zagnieżdżone relacje. W takim przypadku należy skorzystać z notacji z kropką
Lista pól relacji może zawierać pola wielojęzyczne
# Sortowanie
Sortowanie wyników wyszukiwania możliwe jest poprzez przesłanie parametrów (query string) order_by oraz order_direction.
Domyślnie wyniki wyszukiwania sortowane są malejąco po polu id.
# Sortowanie po polach modelu
Core pozwala sortować wyniki wyszukiwania modeli po wszystkich kolumnach modelu w bazie danych oraz polach tłumaczalnych.
# Sortowanie po polach relacji
Możliwe jest również sortowanie modeli po polach relacji (pola z powiązanych tabel).
Aby tego dokonać należy dodać metodę modelu public customOrders i odpowiednio ją skonfigurować.
protected function customOrders(): array
{
return [
// class based filter
'role_name' => OrderByLatestRoleName::class,
// or anonymous function
'role_slug' => fn ($builder, $orderDirection) => $builder->orderByLatestRoleSlug($orderDirection),
];
}
TIP
Więcej na temat query scopes (opens new window) możesz przeczytać w dokumentacji Laravela.
# Przykład sortowania po polach translacji dla aktualnego języka
use Illuminate\Database\Eloquent\Builder;
protected function customOrders(): array
{
return [
'role_name' => fn ($builder, $orderDirection) => $builder
->leftJoin('role_translations', fn ($join) => $join
->on('roles.id', '=', 'role_translations.role_id')
->where('role_translations.locale', app()->getLocale()))
->orderBy('role_translations.name', $orderDirection)
];
}
TIP
Aby dynamicznie pobrać nazwę tabeli powiązanego modelu można skorzystać ze statycznej metody zdefiniowanej w BaseModel → getTableName
$roleTranslationTable = RoleTranslation::getTableName();
Dowiedz się więcej na temat złączeń tabel w Laravelu (opens new window)
# Walidacja
Modele rozszerzające abstrakcyjną klasę BaseModel są walidowane przed zapisaniem w bazie danych.
Reguły walidacji można zdefiniować w modelu, korzystając z metody rules.
public function rules(): array
{
return [
'name' => 'required',
];
}
Nazwy pól walidacji należy przetłumaczyć w metodzie attributes.
public function attributes(): array
{
return [
'name' => __('user::users.attributes.name'),
];
}
Natomiast własne tłumaczenia informacji zwracanych przez walidator należy zdefiniować w metodzie messages.
public function messages(): array
{
return [
'name.required' => __('user::users.messages.name is required'),
];
}
TIP
Walidacja przed zapisaniem można wyłączyć ustawiając własność validateBeforeSave na wartość false.
protected $validateBeforeSave = false;
# Generowanie slugów
Aby obsłużyć tworzenie automatyczne slugów należy dodać do modelu trait Modules\Core\Traits\Sluggable.
Domyślnie slug generowany jest na podstawie pola zdefiniowanego w pliku konfiguracyjnym Modules\Core\Config\config.php pod indeksem default-sluggable .
Jeśli chcemy, aby slug był generowany z innego pola należy dodać do modelu właność protected $sluggable.
TIP
Własność $sluggable może zawierać listę pól, z których zostanie wygenerowany slug.
protected $sluggable = ['first_name', 'last_name'];
Domyślnie implementując trait Sluggable, pole slug jest wymagane i musi być unikalne.
Można nadpisać te zasady dodając do metody modelu rules pozycję slug.
W przypadku gdy zdefiniowana jest reguła unique, a slug się powtórzy, dodawany jest suffix z kolejną liczbą (np. test-1, test-2, itd.).
# Ustawianie kolejności
W przypadku potrzeby ustawiania odpowiedniej kolejności modeli, należy dodać do modelu trait Modules\Core\Traits\Sortable.
Podczas zapisywania modelu pozycja jest ustawiana automatycznie.
W przypadku zmiany wartości któregoś z pól wymienionych we własności $sortableGroup, pozycja również ulega zmianie (do modelu zostaje przypisana ostatnia pozycja w nowej grupie).
# Konfiguracja
Domyślnym polem, które będzie stanowić o kolejności jest order, które można zmienić globalnie dla całej aplikacji w pliku konfiguracyjnym Modules\Core\Config\config.php pod indeksem default-sortable-field lub dla konkretnego modelu definiując własność protected $sortableAttribute.
TIP
Model, który korzysta z trait Sortable domyślnie sortowany jest rosnąco po zdefiniowanym polu $sortableAttribute lub globalnie zdefiniowanym atrybutcie (default-sortable-field)
protected $sortableAttribute = 'position';
Jest również możliwość zdefiniowania grupy, względem której modele będą sortowane.
Ta opcja przydaje się w przypadku, gdy modele mają strukturę drzewa i potrzebujemy ustawiać ich kolejność, np. względem "rodzica" (taką strukturę mają zwykle kategorie czy pozycje menu).
protected $sortableGroup = 'parent_id';
TIP
Własność $sortableGroup może zawierać listę pól
# Zmiana pozycji modelu
Aby ustawić pozycję modelu należy skorzystać z metody setPosition, która jest zdefiniowana w Sortable trait.
$model->setPosition($position);
# Publiczne dane
Domyślnie regułą publicznego modelu jest pole active ustawione na wartość true. Jeśli model posiada tłumaczenia językowe to dodatkową regułą jest pole tłumaczenia translation_active ustawione na wartość true.
W przypadku niestandardowych reguł należy stworzyć metodę (scope) o nazwiescopePublished, która będzie zawierać niestandardowe reguły publicznych treści.
use Illuminate\Database\Eloquent\Builder;
public function scopePublished(Builder $builder): Builder
{
return $builder
->whereActive(true)
->whereTranslation('translation_active', true, app()->getLocale())
->where('published_at', '>=', now());
}
Następnie nazwę klasy modelu należy dodać tablicy w pliku konfiguracyjnym publishable.php.
// config/publishable.php
return [
\Modules\Page\Models\Page::class,
];
Ostatnim krokiem jest dodanie middleware Modules\Core\Http\Middleware\PublishedScope do routingu.
// Modules/Page/Providers/RouteServiceProvider.php
protected function mapApiRoutes()
{
Route::prefix('api/public')
->middleware(['api', 'bindings', PublishedScope::class])
->group(__DIR__ . '/../Http/Routes/public-pages.php');
}