[[oktatas:web:back-end_framework:laravel|< Laravel]]
====== Laravel REST API Drink példa ======
* **Szerző:** Rékási József
* Copyright (c) 2024, Rékási József
* Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC Attribution-Share Alike 4.0 International]]
* Web: https://szit.hu
=====Szükséges szoftverek=====
===Windowson===
*PHP
*XAMP
*Composer
*Visual Studio Code vagy más szerkesztő program
*Insomnia vagy Postman
===Linuxon===
*PHP
*Composer
*Mariadb szerver
*Visual Studio Code vagy más szerkesztő program
*Insomnia vagy Postman
===Linuxon szükséges csomagok===
apt install php phpunit php-bcmath php-mbstring php-xml curl php-curl php-mysql
Ez a példaprojekt egy RestApi alkalmazást mutat be amely egy italraktár adatbázis nyilvántartását kezeli. A projekt Laravel 10 verzióval készült.
=====Adatbázis=====
**__drinks__** ( id, drink, amount, type_id, package_id )
**__types__** ( id, type )
**__packages__** ( id, package )
=====Projekt létrehozás=====
composer create-project laravel/laravel Drink
=====Migrációs állományok=====
Az adatbázisnak üresen léteznie kell, az adattáblákat a megfelelő mezőkkel a migrációs állományok fogják létrehozni.
====drinks====
php artisan make:migration create_drinks_table
A drinks táblában tároljuk az italokat.
public function up(): void {
Schema::create('drinks', function (Blueprint $table) {
$table->id();
$table->string( "drink" );
$table->integer( "amount" );
$table->foreignId( "type_id" );
$table->foreignId( "packages_id" );
$table->timestamps();
});
}
====types====
php artisan make:migration create_types_table
A types táblában tároljuk az italok típusait ( Bor, Sör, üdítő, ...stb )
public function up(): void {
Schema::create('types', function (Blueprint $table) {
$table->id();
$table->string( "type" );
});
}
====packages====
php artisan make:migration create_packages_table
A packages táblában tároljuk az italok kiszereléseit ( Üveges, Hordós, ...stb )
public function up(): void {
Schema::create('packages', function (Blueprint $table) {
$table->id();
$table->string( "package" );
});
}
Adattáblák létrehozása:
php artisan migrate
=====Modellek=====
Az adattáblákhoz tartozó modellek fogják kezelni az adatmozgást az alkalmazás és az adatbázis között. Fel kell venni az írható mezők listáját és szükség szerint ignorálni kell a timestamps mezőt. A model nevének meg kell egyeznie az adattábla nevével de egyes számban.
====Drink====
php artisan make:model Drink
class Drink extends Model {
use HasFactory;
protected $fillable = [
"drink",
"amount",
"type_id",
"package_id"
];
public function type() {
return $this->belongsTo( Type::class );
}
public function package() {
return $this->belongsTo( Package::class );
}
}
====Type====
php artisan make:model Type
class Type extends Model {
use HasFactory;
protected $fillable = [
"type"
];
public $timestamps = false;
public function drink() {
return $this->hasMany( Drink::class );
}
}
====Package====
php artisan make:model Package
class Package extends Model {
use HasFactory;
protected $fillable = [
"package"
];
public $timestamps = false;
public function drink() {
return $this-hasMany( Drink::class );
}
}
=====Resources osztályok=====
A resources osztályok állítják össze a válaszban küldött adathalmazt. Ezeket az adatokat mi adjuk meg, mit szeretnénk látni a kimeneten. A resources osztályok neve megegyezik a model nevével.
php artisan make:resource Drink
Töltsük fela toArray() metódus tömbjét az adathalmazzal.
====DrinkResource====
public function toArray(Request $request): array {
return [
"id" => $this->id,
"drink" => $this->drink,
"amount" => $this->amount,
"type" => $this->type->type,
"package" => $this->package->package
];
}
====TypeResource====
php artisan make:resource Type
public function toArray(Request $request): array {
return [
"type" => $this->type
];
}
====PackageResource====
php artisan make:resource Package
public function toArray(Request $request): array {
return [
"package" => $this->package
];
}
=====Kontrollerek=====
A kontrollerek metódusaiban történik a vezérlés, a végpontok ezekre a metódusokra mutatnak. A vezérlő osztályoknak érdemes külön könyvtárat létrehozni. Ezt könyvtárat a php artisan automatikusan létrehozza, ha nem létezik. A válaszok küldését mivel majdnem minden művelet visszatér valamilyen
eredménnyel érdemes külön osztályba szervezni a kódismétlés kiküszöbölése
végett.
====ResponseController====
php artisan make:controller Api/ResponseController
Két metódust tartalmaz, a sikeres és a hibás művletek eredményét adja vissza.
===Sikeres művelet===
public function sendResponse( $data, $message ) {
$response = [
"sucess" => true,
"data" => $data,
"message" => $message
];
return response()->json( $response, 200 );
}
===Hibás művelet===
public function sendError( $error, $errorMessages = [], $code = 404 ) {
$response = [
"success" => false,
"message" => $error
];
if( !empty( $errorMessages )) {
$response[ "data" ] = $errorMessages;
}
return response()->json( $response, $code );
}
====DrinkController====
php artisan make:controller Api/DrinkController
A DrinkControllert származtassuk a ResponseControllertől. A ResponseControllert és a modelt fel kell venni útvonalra. Kérjük le a drinks tábla adatait.
use App\Http\Controllers\Api\ResponseController;
use App\Models\Drink;
class DrinkController extends ResponseController {
public function getDrinks() {
$drinks = Drink::all();
return $this->sendResponse( $drinks, "Adatok betöltve" );
}
}
Teszteljük Insomnia vagy Postman programban. Ez a metódus minden adatot visszaad a táblából, de az idegen tábláknak csak az id idegen kulcsai látszanak. Kapcsoljuk hozzá a két másik táblát és nézzük meg az eredményt.
public function getDrinks() {
$drinks = Drink::with( "type", "package" )->get();
return $this->sendResponse( $drinks, "Adatok betöltve" );
}
===Egy ital lekérése===
Most használjuk a létrehozott resources osztályt az adatok megjelenítésére.
Az osztályt be kell emelni a kontrollerbe. Mivel a model és a resorces osztály neve megegyezik ezért el kell látnunk aliasszal, hogy a hivatkozás egyértelmű legyen.
use App\Http\Resorces\Drink as DrinkResource;
public function getOneDrink( $id ){
$drink = Drink::with( "type", "package" )->find( $id );
if( is_null( $drink )){
return $this->sendError( "Nincs ilyen ital" );
}
return $this->sendResponse( new DrinkResource( $drink ), "Ital kiválasztva" );
}
===Ital hozzáadása===
Itt szükség van a kapott ital típusának és a kiszerelésének az azonosítójára. Ezeket egy másik adattáblából kell lekérni, ezt az adott adattábla saját kontrollere fogja szolgáltatni. A beérkező adatokat validálni kell. A validálást a saját request osztály végzi. Ezt fel kell venni útvonalra.
use App\Http\Requests\DrinkAddChecker;
public function addDrink( DrinkAddChecker $request ) {
$request->validated();
$input = $request->all();
$drink = new Drink;
$drink->drink = $input["drink"];
$drink->amount = $input["amount"];
$drink->type_id = ( new TypeController )->getTypeId( $input[ "type" ]);;
$drink->package_id = ( new PackageController )->getPackageId( $input[ "package" ]);
$drink->save();
return $this->sendResponse( new DrinkResource( $drink ), "Ital kiírva" );;
}
===Italok frissítése===
Az adatokat itt is validálni kell, a szükséges azonosítokat az adattábla saját kontrollere szolgáltatja.
public function updateDrink( DrinkAddChecker $request, $id ) {
$request->validated();
$input = $request->all();
$drink = Drink::find( $id );
$drink->drink = $input[ "drink" ];
$drink->amount = $input[ "amount" ];
$drink->type_id = ( new TypeController )->getTypeId( $input[ "type" ]);
$drink->package_id = ( new PackageController )->getPackageId( $input[ "package" ]);
$drink->update();
return $this->sendResponse( new DrinkResource( $drink ), "Ital frissítve" );
}
===Ital törlése===
public function deleteDrink( $id ) {
$drink = Drink::find( $id );
$drink->delete();
return $this->sendResponse( new DrinkResource( $drink ), "Ital törölve" );
}
====TypeController====
Ez a vezérlő felelős a types tábla adatkezeléséért a megfelelő metódusokon keresztül. Az osztály szintén a ResponseControllertől öröklődik és be kell emelni a szükséges más osztályokat is.
use App\Models\Type;
use App\Http\Controllers\api\ResponseController as ResponseController;
use App\Http\Resources\Type as TypeResource;
use App\Http\Requests\TypeAddChecker;
===Típusok lekérése===
public function getTypes() {
$types = Type::all();
return $this->sendResponse( $types, "Típusok betöltve" );
}
===Új típus felvétele===
Az adatot itt is validálni kell, ezt a saját validáló osztály végzi.
public function addType( TypeAddChecker $request ) {
$request->validated();
$input = $request->all();
$type = Type::create( $input );
return $this->sendResponse( new TypeResource( $type ), "Sikeres kiírás" );
}
===Típus frissítése===
public function updateType( TypeAddChecker $request, $id ) {
$request->validated();
$input = $request->all();
$type = Type::find( $id );
$type->type = $input[ "type" ];
$type->update();
return $this->sendResponse( new TypeResource( $type ), "Típus frissítve" );
}
===Típus törlése===
public function deleteType( $id ) {
$type = Type::find( $id );
if( is_null( $type )) {
return $this->sendError( "Nincs ilyen típus" );
}else {
$type->delete();
return $this->sendResponse( new TypeResource( $type ), "Típus törölve" );
}
}
===Típus azonosító lekérése===
public function getTypeId( $typeName ) {
$type = Type::where( "type", $typeName )->first();
$id = $type->id;
return $id;
}
====PackageController====
Ez a vezérlő felelős a kiszerelések adatkezeléséért. Örökölni kell a ResponseControllert és ide is be kell emelni a szükséges osztályokat.
use App\Models\Package;
use App\Http\Controllers\api\ResponseController as ResponseController;
use App\Http\Resources\Package as PackageResource;
use App\Http\Requests\PackageAddChecker;
===Kiszerelések lekérése===
public function getPackages() {
$packages = Package::all();
return $this->sendResponse( $packages, "Kiszerelések betöltve" );
}
===Új kiszerelés felvétele===
public function addPackage( PackageAddChecker $request ) {
$request->validated();
$input = $request->all();
$package = Package::create( $input );
return $this->sendResponse( new PackageResource( $package ), "Sikeres kiírás" );
}
===Kiszerelés frissítése===
public function updatePackage( PackageAddChecker $request, $id ) {
$request->validated();
$input = $request->all();
$package = Package::find( $id );
$package->package = $input[ "package" ];
$package->update();
return $this->sendResponse( new PackageResource( $package ), "Kiszerelés frissítve" );
}
===Kiszerelés törlése====
public function deletePackage( $id ) {
$package = Package::find( $id );
if( is_null( $quantity )) {
return $this->sendError( "Nincs ilyen kiszerelés" );
}else {
$package->delete();
return $this->sendResponse( new PackageResource( $package ), "Kiszerelés törölve" );
}
}
===Kiszerelés azonosító lekérése===
public function getPackageId( $packageName ) {
$package = Package::where( "package", $packageName )->first();
$id = $package->id;
return $id;
}
=====Request osztályok=====
Ezek az osztályok felelősek a beérkező adat ellenőrzéséért. A rules() metódus ellenőrzi az adatokat, a messages() metódus a saját hibaüzenetet generálja és a failedValidation() metódus a hibaüzenetet küldi. Be kell emelni két külső osztályt.
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
====DrinkAddChecker====
php artisan make:request DrinkAddChecker
public function rules(): array {
return [
"drink" => "required",
"amount" => "required",
"type" => "required",
"package" => "required"
];
}
public function messages() {
return [
"drink.required" => "Név elvárt",
"amount.required" => "Mennyiség elvárt",
"type.required" => "Típus elvárt",
"packages.required" => "Kiszerelés elvárt",
];
}
public function failedValidation( Validator $validator ) {
throw new HttpResponseException( response()->json([
"success" => false,
"message" => "Adatbeviteli hiba",
"data" => $validator->errors()
]));
}
====TypeAddChecker====
php artisan make:request TypeAddChecker
public function rules(): array {
return [
"type" => "required"
];
}
public function messages() {
return [
"type.required" => "Név elvárt"
];
}
public function failedValidation( Validator $validator ) {
throw new HttpResponseException( response()->json([
"success" => false,
"message" => "Adatbeviteli hiba",
"data" => $validator->errors()
]));
}
====PackageAddChecker====
php artisan make:request PackageAddChecker
public function rules(): array {
return [
"package" => "required"
];
}
public function messages() {
return [
"package.required" => "Kiszerelés elvárt"
];
}
public function failedValidation( Validator $validator ) {
throw new HttpResponseException( response()->json([
"success" => false,
"message" => "Adatbeviteli hiba",
"data" => $validator->errors()
]));
}
====UserRegisterChecker====
php artisan make:request UserRegisterChecker
Ez az osztály végzi a bevitt adatok ellenőrzését a megfelelő szabályokon keresztül. A rules() metódus a szabályokat tartalmazza, a messages() metódus a saját hibaüzeneteket és a failedValidation() metódus küldi a hibaüzeneteket.
Emeljük be a szükséges osztályokat
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
public function rules(): array {
return [
"name" => "required",
"email" => "required",
"password" => "required",
"confirm_password" => "required|same:password"
];
}
public function messages() {
return [
"name.required" => "Név kötelező",
"email.required" => "Email kötelező",
"password.required" => "Jelszó kötelező",
"confirm_password.same" => "Nem egyező jelszó"
];
}
public function failedValidation( Validator $validator ) {
throw new HttpResponseException( response()->json([
"success" => false,
"message" => "Adatbeviteli hiba",
"data" => $validator->errors()
]));
}
====UserLoginChecker====
php artisan make:request UserLoginChecker
Ez az osztály végzi a bejelentkezés adatainak az ellenőrzését, ide is be kell emelni a szükséges osztályokat.
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
public function rules(): array {
return [
"name" => "required",
"password" => "required"
];
}
public function messages() {
return [
"name.required" => "Név kötelező",
"password.required" => "Jelszó kötelező"
];
}
public function failedValidation( Validator $validator ) {
throw new HttpResponseException( response()->json([
"success" => false,
"message" => "Adatbeviteli hiba",
"data" => $validator->errors()
]));
}
=====Útvonalak=====
Az útvonalakat az App\routes\api.php állományban rögzítsük. Az állományban fel kell venni útvonalra a kontrollereket.
use App\Http\Controllers\DrinkController;
use App\Http\Controllers\api\TypeController;
use App\Http\Controllers\api\QuantityController;
use App\Http\Controllers\api\AuthController;
====Italok====
//Italok lekérése
Route::get( "/drinks", [ DrinkController::class, "getDrinks" ]);
//Egy ital lekérése
Route::get( "/oneDrink/{id}", [ DrinkController::class, "getOneDrink" ]);
//Új ital felvétele
Route::post( "/addDrink", [ DrinkController::class, "addDrink" ]);
//Ital frissítése
Route::put( "/updatedrink/{id}", [ DrinkController::class, "updateDrink" ]);
//Ital törlése==
Route::delete( "/deleteDrink/{id}", [ DrinkController::class, "deleteDrink" ]);
====Típusok====
//Típusok lekérése
Route::get( "/types", [ TypeController::class, "getTypes" ]);
//Új típus felvétele
Route::post( "/addtype", [ TypeController::class, "addType" ]);
//Típus frissítése
Route::put( "/updatetype/{id}", [ TypeController::class, "updateType" ]);
//Típus törlése
Route::delete( "/deletetype/{id}", [ TypeController::class, "deleteType" ]);
====Kiszerelések====
//Kiszerelések lekérése
Route::get( "/packages", [ PackageController::class, "getPackages" ]);
//Új kiszerelés felvétele
Route::post( "/addpackage", [ PackageController::class, "addPackage" ]);
//Kiszerelés frissítése
Route::put( "/updatepackage/{id}", [ PackageController::class, "updatePackage" ]);
//Kiszerelés törlése
Route::delete( "/deletepackage/{id}", [ PackageController::class, "deletePackage" ]);
=====Azonosítás=====
A felhasználók kezeléséhez szükség van sanctum csomagra
composer require laravel/sanctum
A sanctum kiszolgáló beállítása:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
A Kernel.php állományban engedélyezni kell a csoportos middleware api szekciójában a Sanctum használatát. Vegyük ki a kommentet a megfelelő sor elől.
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api'=>
//\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
A felhasználók kezelése szintén adatbázisból történik, az ehhez szükséges adat táblák, a users és a personal_access_tokens a migráció során automatikusan létrejönnek. Szintén automatikusan létrejön a User model is, ebben a szükséges három mező: name, email, password. Ha User modelben nem találjuk, emeljük be a HasApiTokens osztályt.
use Laravel\Sanctum\HasApiTokens
A users táblát ki kell egészíteni két új mezővel amelyben a bejelentkezések számát és az esetleges kitiltás lejárati idejét tároljuk.
php artisan make:migration add_banned_data_to_users_table
public function up(): void {
Schema::table('users', function (Blueprint $table) {
$table->timestamp( "login_attempts" )->default( 0 );
$table->timestamp( "banned_time" )->nullable()->default( null );
});
}
====UserController====
Ez a vezérlő felelős a felhasználók regisztrációjáért, a bejelentkezésekért és a kijelentkezésekért. Négy metódusa van a register(), a logIn(), a logOut() és az isValidUser(). A kontrollert örököltessük a ResponseControllertől és emeljük be, illetve a még szükséges osztályokat is.
use App\Http\Controllers\Api\ResponseController as ResponseController;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use App\Http\Requests\UserRegisterChecker;
use App\Http\Requests\UserLoginChecker;
use Carbon\Carbon;
Az adatokat itt is validálni kell, ezt a saját ellenőrző osztály végzi.
public function register( UserRegisterChecker $request ) {
$request->validated();
$input = $request->all();
$input[ "password" ] = bcrypt( $input[ "password" ]);
$user = User::create( $input );
$success[ "name" ] = $user->name;
return $this->sendResponse( $success, "Sikeres regisztrácio" );
}
Ellenőrizni kell, hogy a bejelentkezni próbáló felhasználó létezik-e. Ha létezik meghívja a login() metódust, ha nem létezik hibaüzenetet ad.
public function isValidUser( UserLoginChecker $request ) {
$count = User::where( "name", $request->name )->count();
if( $count != 0 ) {
$data = $this->login( $request );
return $data;
}else {
return $this->sendError( "Azonosítási hiba", [ "Nincs ilyen felhasználó" ] );
}
}
Most következik a felhasználó ellenőrzése, a felhasználónév jelszó páros. Ha a felhasználó háromszor hibásan adja meg a jelszavát, egy percre tíltásba kerül és hibaüzenetet kap. A felhasználó nevét és a tiltás idejének lejáratát emailben elküldjük egy adott címre. Sikeres bejelentkezés esetén generálni kell egy tokent ami a personal_access_tokens táblában tárolódik és nullázni kell a hibás bejelentkezés számlálókat. A hibás próbálkozások számlálását és rögzítését a BanningTimeController végzi.
public function login( UserLoginChecker $request ) {
$bannedTime = null;
$request->validated();
if(( Auth::attempt([ "name" => $request->name, "password" => $request->password ]))) {
$bannedTime = ( new BanningTimeController )->getUserBannedTime( $request->name );
if( $bannedTime > Carbon::now( "Europe/Budapest" ) ) {
return $this->sendError( "Túl sok próbálkozás", [
"nextlogin" => $bannedTime
], 429 );
}
( new BanningTimeController )->resetBannedData( $request->name );
$authUser = Auth::user();
$success[ "token" ] = $authUser->createToken( $authUser->name."Token" )->plainTextToken;
$success[ "name" ] = $authUser->name;
return $this->sendResponse( $success, "Sikeres bejelentkezés" );
}else {
$loginCounter = ( new BanningTimeController )->getLoginAttempts( $request->name );
if( $loginCounter < 3 ) {
( new BanningTimeController )->setLoginAttempts( $request->name );
return $this->sendError( "Sikertelen azonosítás", [ "error" => "Hibás felhasználónév vagy jelszó" ]);
}else if( $loginCounter == 3 && is_null( $bannedTime )) {
$bannedTime = ( new BanningTimeController )->setUserBannedTime( $request->name );
$allert = ( new AllertController )->sendEmail( $request->name, $bannedTime );
return $this->sendError( "Sikertelen azonosítás", [ "error" => "Túl sok próbálkozás" ]);
}
}
}
Kijelentkezéskor a tokent törölni kell.
public function logOut( Request $request ){
auth("sanctum")->user()->currentAccessToken()->delete();
return response()->json("Sikeres kijelentkezés");
}
====BanningTimeController====
Ennek a vezérlőnek a feladata kezelni a hibás bejelentkezések rögzítését és a kitiltás időintervallumának a beállítását. Emeljük be a szükséges osztályokat.
use Carbon\Carbon;
use App\Models\User;
class BanningTimeController extends Controller {
public function getUserBannedTime( $name ) {
$user = User::where( "name", $name )->first();
return $user->banned_time;
}
public function getLoginAttempts( $name ) {
$user = User::where( "name", $name )->first();
$loginAttempts = $user->login_attempts;
return $loginAttempts;
}
public function setLoginAttempts( $name ) {
User::where( "name", $name )->increment( "login_attempts" );
}
public function setUserBannedTime( $name ) {
$user = User::where( "name", $name )->first();
$banned_time = Carbon::now( "Europe/Budapest" )->addSeconds( 60 );
$user->banned_time = $banned_time;
$user->save();
return $banned_time;
}
public function resetBannedData( $name ) {
$user = User::where( "name", $name )->first();
$user->login_attempts = 0;
$user->banned_time = null;
$user->save();
}
}
=====Email küldése=====
Két osztályra és egy nézetre van szükség, ezeket generáljuk le.
php artisan make:controller Api\AllertController
php artisan make:mail AllertMail
A resources\views könyvtárban hozzuk létre a mail.blade.php állományt.
Az .env állományban állítsuk be az email küldéséhez szükséges adatokat. A példában gmail fiókot használunk.
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=a_sajat_fiokod@gmail.com
MAIL_PASSWORD=a_titkos_jelszo
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="drink@laravel.com" //Ez az emailcím fog megjelenni a levélben.
MAIL_FROM_NAME="Drink projekt" // Ez a név fog megjelenni a levében.
====AllertController====
Ennek a vezérlőnek a feladata összeállítani az email tartalmát és itt adhatjuk meg a címzettet. Emeljük be a szükséges osztályokat.
use App\Mail\AllertMail;
use Illuminate\Support\Facades\Mail;
class AllertController extends Controller {
public function sendEmail( $user, $time ) {
$content = [
"title" => "Felhasználó blokkolva",
"user" => $user,
"time" => $time
];
Mail::to( "a_sajat_email_cimed@gmail.com" )->send( new AllertMail( $content ));
return false;
}
}
====AllertMail====
A konstruktorban vegyük át az összeállított adatokat. Az envelop() metódusban adhatjuk meg az email tárgyát. A content() metódusban hivatkozunk a nézetre amiben a levél kinézete van. Az attachments() metódusban van lehetőség állomány csatolására.
class AllertMail extends Mailable {
use Queueable, SerializesModels;
public $content;
public function __construct( $content ) {
$this->content = $content;
}
public function envelope(): Envelope
{
return new Envelope(
subject: 'Allert Mail',
);
}
public function content(): Content
{
return new Content(
view: 'mail',
);
}
public function attachments(): array
{
return [];
}
}
====Mail nézet====
Ebben a nézetben kerül összeállításra a levél kinézete, ide emeljük be a küldeni kívánt adatokat és írhatunk hozzá további szöveget.
{{ $content[ "title" ] }}
Idő: {{ $content[ "time" ] }}
{{ $content[ "user" ] }}
1 percre kitiltva túl sok sikertlen próbálkozás miatt.
Route::group([ "middleware" => [ "auth:sanctum" ]], function() {
Route::post( "/logout", [ AuthController::class, "logOut" ]);
Route::post( "/addDrink", [ DrinkController::class, "addDrink" ]);
Route::put( "/updatedrink/{id}", [ DrinkController::class, "updateDrink" ]);
Route::delete( "/deletedrink/{id}", [ DrinkController::class, "deleteDrink" ]);
Route::post( "/addtype", [ TypeController::class, "addType" ]);
Route::put( "/updatetype/{id}", [ TypeController::class, "updateType" ]);
Route::delete( "/deletetype/{id}", [ TypeController::class, "deleteType" ]);
Route::post( "/addpackage", [ PackageController::class, "addPackage" ]);
Route::put( "updatepackage/{id}", [ PackageController::class, "updatePackage" ]);
Route::delete( "/deletepackage/{id}", [ PackageController::class, "deletePackage" ]);
});
Route::post( "/register", [ AuthController::class, "register" ])->middleware( "throttle:100,43200" );;
Route::post( "/login", [ AuthController::class, "isValidUser" ])->middleware( "throttle:100,43200" );
Route::get( "/drinks", [ DrinkController::class, "getDrinks" ]);
Route::get( "/oneDrink/{id}", [ DrinkController::class, "getOneDrink" ]);
Route::get( "/types", [ TypeController::class, "getTypes" ]);
Route::get( "/quantities", [ QuantityController::class, "getQuantities" ]);