Címkék: #Component #Digitalizáció (Digitalization) #Érvényesítés (Validation) #Filament #Laravel #Nézet (View) #Többnyelvűsítés (Localization) #Űrlap (Form)
Dokumentumokra azért lesz szükség, mert ezek alapján lehet karbantartani, javítani a berendezéseket. Ezzel rögtön meg is van, hogy a berendezésekkel kell majd összekapcsolnunk őket (egy berendezéshez több dokumentum is tartozhat).
Hozzuk létre a model és a migrációs fájlt hozzá!
php artisan make:model Document -m
A migrációs fájl up() metódusának tartalma:
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->foreignId('device_id')->constrained()->onUpdate('cascade')->onDelete('cascade');
$table->text('attachment');
$table->timestamps();
});
Migráljuk a táblát (php artisan migrate)! Majd ezek alapján a Document model fájl tartalma:
protected $fillable = ['name', 'device_id', 'attachment'];
public function device(): BelongsTo
{
return $this->belongsTo(Device::class);
}
A kapcsolat másik oldalán, a Device model fájlban is definiáljuk a kapcsolatot:
public function documents(): HasMany
{
return $this->hasMany(Document::class);
}
A helyes működéshez az importálást (HasMany) hajtsuk végre az osztály előtt!
Hozzuk létre az erőforrást hozzá megtekintés nézettel együtt!
php artisan make:filament-resource Document --view
Alapinformációk:
protected static ?string $navigationIcon = 'heroicon-o-document-text';
public static function getNavigationGroup(): string
{
return __('module_names.navigation_groups.administration');
}
public static function getModelLabel(): string
{
return __('module_names.documents.label');
}
public static function getPluralModelLabel(): string
{
return __('module_names.documents.plural_label');
}
Ehhez nyilván kellenek a module_names szótárban az új bejegyzések például a hu mappában lévő module_names.php-ban:
'documents' => [
'label' => 'Dokumentum',
'plural_label' => 'Dokumentumok',
],
És ugyanígy az en mappában lévő változatnál is, csak természetesen angolul.
Először tekintsük át, hogy mit érdemes eltárolni a dokumentumokról:
Magának az erőforrásnak a form() metódusa, illetve a visszatérési $form eleme így nézhet ki:
return $form->schema([
Forms\Components\Section::make()->schema([
Forms\Components\TextInput::make('name')->label(__('fields.name'))
->required()
->unique(ignoreRecord: true)
->maxLength(255),
Forms\Components\Select::make('device_id')->label(__('module_names.devices.label'))
->relationship('device', 'name')
->searchable()
->preload()
->required(),
Forms\Components\FileUpload::make('attachment')->label(__('fields.attachment'))
->required()
->preserveFilenames()
->openable()
->downloadable()
->maxSize(20000),
])
]);
Nézzük meg őket részletesen:
Az eredmény pedig itt látható:
Az űrlap teljesen jól néz így ki, a fájlfeltöltéshez használt mezőt a Filament alakítja a számunkra ilyen barátságosra.
A mezők kitöltése után a (tetszőlegesen kiválasztott) fájlt drag&drop is rá lehet helyezni a fájl mezőre és a következőt kell látnunk:
A "Feltöltés" befejezése után pedig megváltozik a színe és kiírja, hogy "Sikeres feltöltés":
Megnyomhatjuk a "Létrehozás" gombot és el is menti így a dokumentumot.
Utána bár még nem látszódik jól a táblázat oszlopai és sorai, de az igen, hogy egy sor van benne és ha annak a Szerkesztés gombjára kattintunk akkor újra megnyílik az imént létrehozott dokumentum bejegyzés. Ekkor viszont már megjelennek további gombok a fájl neve mellett balra, amelyekkel sorrendben: 1. törölni, 2. új lapon megnyitni, és 3. letölteni tudjuk ezt a fájlt (kiemeltem ezeket a gombokat alább).
Ha 404-es Not Found HTTP státuszkódot kapunk a megnyitáskor és a letöltéskor is, akkor szükség van arra, hogy összekössük a feltöltött fájlok helyét (storage / app / public mappa) a nyilvánosan elérhető public mappával:
php artisan storage:link
Kezdjük a táblázat oszlopaival ($table->columns([])).
Tables\Columns\TextColumn::make('id')->searchable()->sortable(),
Tables\Columns\TextColumn::make('name')->label(__('fields.name'))
->searchable()->sortable(),
Tables\Columns\TextColumn::make('device.name')->label(__('module_names.devices.label'))
->searchable()->sortable(),
Tables\Columns\TextColumn::make('created_at')->label(__('fields.created_at'))
->dateTime('Y-m-d H:i')
->searchable()->sortable(),
Újdonság ezekben nincsen, mert már korábban is így csináltuk.
A "Megtekintés" gomb lesz, ami ahhoz a "view" nézethez vezet, ami majd publikus lesz a felhasználók számára és le tudják tölteni a dokumentumot, illetve látható lesz rajta a QR kód is, ami ide vezeti majd a karbantartó kollégákat, ha kinn az üzemben problémájuk adódna a géppel és erre a dokumentumra lenne szükségük.
A létrejövő CreateDocument.php és EditDocument.php fájlokhoz adjuk hozzá a szokásost: mentés után visszairányítjuk a felhasználót a lista nézethez:
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
Innentől egy kicsit kényelmi funkciókra megyünk rá: új akciók lesznek a táblázatos nézetben
A letöltési linket a view és edit action-ök után adjuk hozzá a táblázat action-jeit tartalmazó tömbhöz:
Tables\Actions\Action::make('download')
->label(__('actions.download'))
->action(function ($record) {
return Storage::download('public/' . $record->attachment);
})
->icon('heroicon-o-document-arrow-down')
->color('primary'),
A többnyelvű működéshez hozzunk létre egy új szótárat a funkcióknak: actions.php néven az en és hu mappákban is: helyezzük el bennük a 'download' kulcsot 'Download' és 'Letöltés' értékekkel. A letöltés akció felépítéséhez és végrehajtásához itt a kódban a Storage::download() metódust használtuk, mivel a fájlok alapértelmezetten ide kerülnek feltöltésre a felhasználó által, de nyilván ha valaki máshova szeretné elhelyezni a fájlokat, esetleg egy külön tárhelyre vagy a felhőbe, akkor ezt a részt még finomhangolni kellene. Ezen felül letöltési ikont és színt is állítottunk be a letöltési linknek.
QR kód elérését a táblázatban funkcióhoz először a DocumentResource.php-ban a getPages() metódusban hozzuk létre a "create" után ezt a sort:
'qr' => Pages\QRDocument::route('/qr/{record}'),
A table() metódusban pedig ugyanúgy, ahogy a berendezéseknél csináltuk korábban az actions() részben a ViewAction és az EditAction után hozzuk létre az új QR kódos akciót, amit akár a berendezésektől is idemásolhatunk, de a "device" nézet nevet cseréljük ki "document"-re, így:
Tables\Actions\Action::make('QR')->label(__('fields.qr_code')) ->modalContent(fn ($record): View => view('filament.resources.document-resource.pages.q-r-document', ['record' => $record])) ->modalSubmitAction(false) ->modalCancelAction(false)
->icon(
'heroicon-o-printer'
)
->color('secondary')
->tooltip(__('actions.print') . ': ' . __('fields.qr_code'))
A tooltip() rész egy kicsit bővebb leírást ("Nyomtatás: QR kód") ad arról, hogy mit is csinál a QR kód link, de a táblázat sorában mégsem foglalunk így neki olyan sok helyet, mint ha a hosszabb verziót teljesen kiírnánk.
Így a QR kód akció is bekerül a táblázat soraihoz. Az eredmény itt látható:
A QR kódhoz tartozó új nézetet a resources / views / filament / resources / document-resource / pages mappába hozzuk létre q-r-document.blade.php névvel. A tartalma pedig legyen a másik QR kódos (device) nézethez nagyon hasonló:
Megjegyzés 1.: az actions.php szótárakhoz a 'print' kulcsot is adjuk hozzá (en: 'Print', hu: 'Nyomtatás') értékekkel és akkor a két akciót már szintén többnyelvűsítve tudjuk használni a jövőben.
Megjegyzés 2.: a Filament QR kódot nyomtató két nézet fájlja rettentő módon hasonlít egymásra, mindössze néhány adattagban (linkben) különböznek egymástól, így érdemes lehet készíteni hozzájuk egy nézet komponenst, amelyet utána felparaméterezve tudunk használni.
Utóbbi megoldáshoz hozzunk létre egy komponenst a resources / views mappában egy új components mappában: print-layout.blade.php névvel.
Ez a kód három helyen tartalmazott dinamikusan változó paramétereket: a <h1> tag-en belül rögtön kettőt is, az egyik az oldal "típusa" lesz, hogy most a berendezéshez vagy a dokumentumhoz irányítjuk majd oda a felhasználót, aki bescanneli a QR kódot. Végül a QrCode generáló metóduson belül, hogy maga az útvonal ($route), amit a QR kód rejt, az mi legyen. Ezekre itt a kódban már létre is hoztam a három változót: $type, $name, $route. A komponens használata során ezeket a változókat fogjuk átadni az attribútumokon keresztül.
Utána pedig a két nagyon hasonló nézetünket módosítsuk: előbb a q-r-document.blade.php-t majd ez alapján önállóan próbáljuk megoldani a q-r-device.blade.php-t is. Töröljük (vagy kommenteljük ki) a fájl eddigi tartalmát és helyette szúrjuk be ezt:
Ez mindössze annyi, hogy használjuk a print-layout.blade.php nézet komponens fájlt, az attribútumokban pedig átadjuk neki a változók értékeit. Kipróbálás után tapasztalhatjuk, hogy tökéletesen működik!
Hozzuk létre a kapcsolatot a berendezéshez, hiszen egy berendezés több dokumentumot is "tartalmazhat", ezért a berendezések megtekintési oldalán hasznos lenne látni a hozzá tartozó dokumentumokat.
php artisan make:filament-relation-manager DeviceResource documents name
Az utasítás hatására létrejött a DeviceResource mappában a RelationManagers mappa és benne a DocumentsRelationManager.php fájl. Mielőtt ezt a fájlt elkezdenénk szerkeszteni, előtte a DeviceResource.php erőforrás fájl getRelations() metódusának visszatérési tömbjéhez adjuk hozzá:
RelationManagers\DocumentsRelationManager::class,
Magát a DocumentsRelationManager osztályt a korábban ismertetett megoldás szerint bővítjük ki.
A kapcsolatot jelző szekció fejlécének címét, illetve majd magát az innen létrehozható új dokumentum helyes feliratát mutató gomb címkéjét mutató funkciókkal kezdjük az osztály szerkesztését:
public static function getTitle(Model $ownerRecord, string $pageClass): string
{
return __('module_names.documents.plural_label');
}
public static function getModelLabel(): string
{
return __('module_names.documents.label');
}
Utána nem a form() metódusának visszatérési tömbjét szerkesztjük, illetve azt üresen hagyjuk (kitöröljük a benne lévő mezőt), hanem a táblázat akcióin (table() metóduson) keresztül irányítjuk a funkciókhoz a felhasználót.
return $table ->recordTitleAttribute('name') ->columns([ Tables\Columns\TextColumn::make('name')->label(__('fields.name')) ->searchable()->sortable(), ]) ->filters([ // ]) ->headerActions([ Tables\Actions\CreateAction::make()->url(fn (): string => DocumentResource::getUrl('create')), ]) ->actions([ Tables\Actions\ViewAction::make()->url(fn (Model $record): string => DocumentResource::getUrl('view', ['record' => $record])), Tables\Actions\EditAction::make()->url(fn (Model $record): string => DocumentResource::getUrl('edit', ['record' => $record])), Tables\Actions\Action::make('download') ->label(__('actions.download')) ->action(function ($record) { return Storage::download('public/' . $record->attachment); }) ->icon('heroicon-o-document-arrow-down') ->color('primary'), Tables\Actions\Action::make('QR')->label(__('fields.qr_code')) ->modalContent(fn ($record): View => view('filament.resources.document-resource.pages.q-r-document', ['record' => $record])) ->modalSubmitAction(false) ->modalCancelAction(false)
->icon(
'heroicon-o-printer'
)
->color('secondary')
->tooltip(__('actions.print') . ': ' . __('fields.qr_code')) ]) ->bulkActions([]) ->emptyStateActions([ Tables\Actions\CreateAction::make()->url(fn (): string => DocumentResource::getUrl('create')), ]);
Egyetlen oszlopot (mezőt) jelenítünk meg a táblázatban, a dokumentum nevét, itt ennél több nem is kell. A headerActions() részben új dokumentumot létrehozó gombot helyezünk el. Az akciók között pedig a megtekintési (view) és szerkesztési (edit) akciókon keresztül irányítjuk el a felhasználót a megfelelő nézetekhez. Az utána jövő akciók: letöltés és QR kód nyomtatás funkciók a korábbi fő DocumentResource fájlból már ismerősek lehetnek. BulkActions() részt hagyjuk üresen, ne adjunk itt lehetőséget a "tömeges" törlésre. Illetve végül, ha valamelyik berendezésnek nem lenne még egyetlen dokumentuma sem, akkor itt az emptyStateActions() részben látható részben a dokumentum létrehozó nézethez irányítjuk a felhasználót.
Egy adott berendezés szerkesztése során így fog kinézni a berendezés adatait szerkesztő űrlap alatti dokumentumokat tartalmazó táblázat:
Ha ugyanezt nem a berendezés szerkesztési oldalán (például: devices/1/edit), hanem csak a megjelenítési oldalán (például: devices/1) keresztül nézzük meg, akkor az iménti táblázatból hiányozni fog az "Új Dokumentum" gomb jobb felülről, illetve a "Szerkesztés" gomb a táblázat sorából.
Logikailag a menü struktúrájában érdemes felcserélni a
berendezéseket és a berendezés típusokat. Ezt a fő erőforrásoknál egy új
attribútum felülírással tudjuk módosítani. Adjuk hozzá mindegyik
erőforrás fő fájljához ezt (talán a navigationIcon után érdemes
beszúrni):
protected static ?int $navigationSort = 3;
A DeviceTypeResource.php-ban legyen az 1, a DeviceResource.php-ban legyen a 2, míg a most készülő DocumentResource.php-ban legyen a 3. Így már a logikának megfelelő lesz a menüpontok sorrendje.
A bejegyzés elején úgy gondoltam, hogy ez egy rövidebb írás lesz, de mégsem így lett, mivel elég sokmindent elvégeztünk közben és 22 fájl is újonnan jött létre vagy módosult a munka során. Ezek a változások ebben a GitHub commit-ben érhetők el.
A közvetlen folytatásban következik a jogosultsági rendszer kiépítése a jogosultságokkal.
Ha egyéni oktatás, mentorálás, vagy fejlesztési projekt kapcsán szeretnél segítséget kérni tőlem, esetleg együttműködni velem, akkor keress meg a weboldalamon található elérhetőségeken keresztül!