Add file upload function. modify paramiter FILESYSTEM_DISK=public in file .env
This commit is contained in:
@@ -12,25 +12,40 @@ use Illuminate\Support\Facades\Storage;
|
|||||||
|
|
||||||
class FileController extends Controller
|
class FileController extends Controller
|
||||||
{
|
{
|
||||||
public function datasubmit(FileRequest $request): RedirectResponse
|
public function uploadFile(FileRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
//upload file in public folder
|
|
||||||
//dd($reqest->all());
|
|
||||||
//$filename = 'new_file.'.$request->filename->extension();
|
|
||||||
//$request->filename->move(public_path('uploads'), $filename);
|
|
||||||
|
|
||||||
//dd($request->all());
|
|
||||||
//$request->validate([
|
|
||||||
// 'filename'=>'required'
|
|
||||||
//]);
|
|
||||||
|
|
||||||
$filename = 'new_file.'.$request->file("filetoinsert")->getClientOriginalExtension();
|
$filename = 'new_file.'.$request->file("filetoinsert")->getClientOriginalExtension();
|
||||||
$request->file("filetoinsert")->storeAs('file_temp', $filename);
|
$request->file("filetoinsert")->storeAs('file_temp', $filename);
|
||||||
|
|
||||||
//$request->filename->storeAs('file_temp', $filename);
|
// Leggi il contenuto del file CSV
|
||||||
|
$contenutoCSV = Storage::get('file_temp/new_file.csv');
|
||||||
|
|
||||||
|
// Analizza il contenuto CSV
|
||||||
|
$righeCSV = str_getcsv($contenutoCSV, "\n");
|
||||||
|
|
||||||
|
// Converte le righe CSV in un array associativo
|
||||||
|
$dati = [];
|
||||||
|
foreach ($righeCSV as $riga) {
|
||||||
|
$dati[] = str_getcsv($riga);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converte l'array in formato JSON
|
||||||
|
$json = json_encode($dati);
|
||||||
|
|
||||||
|
// Stampa il JSON
|
||||||
|
echo $json;
|
||||||
|
|
||||||
|
|
||||||
return redirect('/words');
|
return redirect('/words');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function insertFile()
|
||||||
|
{
|
||||||
|
return view("words.partials.upload-file");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function delete(): RedirectResponse
|
public function delete(): RedirectResponse
|
||||||
{
|
{
|
||||||
Storage::delete('file_temp/new_file.txt');
|
Storage::delete('file_temp/new_file.txt');
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Requests\WordRequest;
|
use App\Http\Requests\StoreWordRequest;
|
||||||
|
use App\Http\Requests\UpdateWordRequest;
|
||||||
use App\Models\Word;
|
use App\Models\Word;
|
||||||
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
@@ -19,14 +20,14 @@ class WordsController extends Controller
|
|||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
return view('words.index', [
|
return view('words.index', [
|
||||||
'words' => Word::all(),
|
'words' => Word::latest('id')->paginate(10),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a new Word in the database.
|
* Store a new Word in the database.
|
||||||
*/
|
*/
|
||||||
public function store(WordRequest $request): RedirectResponse
|
public function store(StoreWordRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$word = new Word;
|
$word = new Word;
|
||||||
$word->name = $request->name;
|
$word->name = $request->name;
|
||||||
@@ -46,21 +47,42 @@ class WordsController extends Controller
|
|||||||
$name = $data->name;
|
$name = $data->name;
|
||||||
$translation = $data->translation;
|
$translation = $data->translation;
|
||||||
//return $translation;
|
//return $translation;
|
||||||
return view("words.modify-word-form", compact('id', "name", "translation"));
|
return view("words.partials.modify-word-form", compact('id', "name", "translation"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insert()
|
||||||
|
{
|
||||||
|
return view("words.partials.insert-word-form");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update an existing Word in the database.
|
* Update an existing Word in the database.
|
||||||
*/
|
*/
|
||||||
public function update(WordRequest $request, $id): RedirectResponse
|
public function update(UpdateWordRequest $request, $id): RedirectResponse
|
||||||
{
|
{
|
||||||
$data = Word::find($id);
|
$data = Word::find($id);
|
||||||
|
|
||||||
$data->name = $request->name;
|
$data->translation = $request->input('translation');
|
||||||
$data->translation = $request->translation;
|
|
||||||
$data->save();
|
$data->save();
|
||||||
|
// Update the unique value
|
||||||
|
$data->name = $request->input('name');
|
||||||
|
|
||||||
return redirect('/words');
|
// Check for uniqueness
|
||||||
|
$isUnique = Word::where('name', $data->name)
|
||||||
|
->where('id', '!=', $id) // Exclude the current record from the uniqueness check
|
||||||
|
->doesntExist();
|
||||||
|
|
||||||
|
if ($isUnique) {
|
||||||
|
// Save the updated record
|
||||||
|
$data->translation = $request->input('translation');
|
||||||
|
$data->save();
|
||||||
|
|
||||||
|
// Redirect to a success route or return a success message
|
||||||
|
return redirect('/words');
|
||||||
|
} else {
|
||||||
|
// Unique constraint violated
|
||||||
|
return back()->withInput()->withErrors(['name' => 'Error: words already exists']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ namespace App\Http\Requests;
|
|||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
class WordRequest extends FormRequest
|
class StoreWordRequest extends FormRequest
|
||||||
{
|
{
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => ['required'],
|
'name' => 'required|string|unique:words,name',
|
||||||
'translation' => ['required', 'unique:words,translation'],
|
'translation' => 'required|string',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
37
app/Http/Requests/UpdateWordRequest.php
Normal file
37
app/Http/Requests/UpdateWordRequest.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class UpdateWordRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'required|string',
|
||||||
|
'translation' => 'required|string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name.required' => 'Insert italian Word',
|
||||||
|
'translation.required' => 'Insert dialect Word',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,5 +11,6 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
class Word extends Model
|
class Word extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
protected $fillable = ['name', 'translation'];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
39
resources/views/components/table-words.blade.php
Normal file
39
resources/views/components/table-words.blade.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
@props(['items' => []])
|
||||||
|
@props(['fields' => []])
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">ID#</th>
|
||||||
|
<th scope="col">Italian Word</th>
|
||||||
|
<th scope="col">Dialect Translation</th>
|
||||||
|
<th scope="col">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
@forelse ($items as $item)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $loop->iteration }}</td>
|
||||||
|
<td>{{ $item->{$fields[0]} }}</td>
|
||||||
|
<td>{{ $item->{$fields[1]} }}</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
@can('edit-user')
|
||||||
|
<a href="edit_record/{{ $item->id }}"><button class="btn btn-primary btn-xs">Edit</button> </a>
|
||||||
|
@endcan
|
||||||
|
@can('delete-user')
|
||||||
|
<a href="delete_record/{{ $item->id }}"><button class="btn btn-danger btn-xs">Delete</button> </a>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<td colspan="5">
|
||||||
|
<span class="text-danger">
|
||||||
|
<strong>No User Found!</strong>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
@@ -4,32 +4,13 @@
|
|||||||
{{ __('Table Words') }}
|
{{ __('Table Words') }}
|
||||||
</h2>
|
</h2>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
|
<div class="card">
|
||||||
<div class="py-12">
|
<div class="card-header">Manage Words Translation</div>
|
||||||
<div class="row">
|
<div class="card-body">
|
||||||
<div class="col-lg-7 col-xl-8 grid-margin stretch-card">
|
<a href="{{ route('words.edit') }}" class="btn btn-success btn-sm my-2"><i class="bi bi-plus-circle"></i> Insert Word</a>
|
||||||
<div class="card">
|
<a href="{{ route('words.insertFile') }}" class="btn btn-success btn-sm my-2"><i class="bi bi-plus-circle"></i> Upload File</a>
|
||||||
<div class="card-body">
|
<x-table-words :items="$words" :fields="array('name','translation')" />
|
||||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
|
||||||
<div class="max-w-xl">
|
|
||||||
@include('words.partials.insert-word-form')
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-5 col-xl-4 grid-margin stretch-card">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
|
||||||
<div class="max-w-xl">
|
|
||||||
@include('words.partials.upload-file')
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{{ $words->links('vendor.pagination.bootstrap-5') }}
|
||||||
</div>
|
</div>
|
||||||
<x-table :items="$words" :fields="array('name','translation')" />
|
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<x-app-layout>
|
|
||||||
<section>
|
|
||||||
<header>
|
|
||||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
|
||||||
{{ __('Insert Word') }}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
{{ __("Enter the word Italian and its dialect translation") }}
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<form method="post" action="{{ route('words.update', $id) }}" class="mt-6 space-y-6">
|
|
||||||
@csrf
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<x-input-label for="name" :value="__('Italian Word')" />
|
|
||||||
<x-text-input id="name" name="name" value="{{$name}}" type="text" class="mt-1 block w-full" placeholder="Italian Word" required autofocus autocomplete="name"/>
|
|
||||||
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<x-input-label for="translation" :value="__('Dialect Word')" />
|
|
||||||
<x-text-input id="translation" name="translation" value="{{$translation}}" type="text" class="mt-1 block w-full" placeholder="Dialectic Word" required autofocus autocomplete="translation" />
|
|
||||||
<x-input-error :messages="$errors->get('translation')" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</x-app-layout>
|
|
||||||
@@ -1,31 +1,42 @@
|
|||||||
<section>
|
<x-app-layout>
|
||||||
<header>
|
<div class="card">
|
||||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
<div class="card-header">
|
||||||
{{ __('Insert Word') }}
|
<div>
|
||||||
</h2>
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||||
|
{{ __('Insert Word') }}
|
||||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
</h2>
|
||||||
{{ __("Enter the word Italian and its dialect translation") }}
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
</p>
|
{{ __("Enter the word Italian and its dialect translation") }}
|
||||||
</header>
|
</p>
|
||||||
|
</div>
|
||||||
<form method="post" action="{{ route('words.insert') }}" class="mt-6 space-y-6">
|
<div class="float-end">
|
||||||
@csrf
|
<a href="{{ route('words.index') }}" class="btn btn-primary"> Back</a>
|
||||||
|
</div>
|
||||||
<div>
|
|
||||||
<x-input-label for="name" :value="__('Italian Word')" />
|
|
||||||
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" placeholder="Italian Word" autofocus autocomplete="name"/>
|
|
||||||
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="card-body">
|
||||||
<x-input-label for="translation" :value="__('Dialect Word')" />
|
<form method="post" action="{{ route('words.insert') }}" class="mt-6 space-y-6">
|
||||||
<x-text-input id="translation" name="translation" type="text" class="mt-1 block w-full" placeholder="Dialectic Word" autofocus autocomplete="translation" />
|
@csrf
|
||||||
<x-input-error :messages="$errors->get('translation')" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-input-label for="name" :value="__('Italian Word')" />
|
||||||
|
<div class="col-md-6">
|
||||||
|
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" placeholder="Italian Word" autofocus autocomplete="name"/>
|
||||||
|
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-input-label for="translation" :value="__('Dialect Word')" />
|
||||||
|
<div class="col-md-6">
|
||||||
|
<x-text-input id="translation" name="translation" type="text" class="mt-1 block w-full" placeholder="Dialectic Word" autofocus autocomplete="translation" />
|
||||||
|
<x-input-error :messages="$errors->get('translation')" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-app-layout>
|
||||||
|
|||||||
41
resources/views/words/partials/modify-word-form.blade.php
Normal file
41
resources/views/words/partials/modify-word-form.blade.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<x-app-layout>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||||
|
{{ __('Insert Word') }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
{{ __("Enter the word Italian and its dialect translation") }}
|
||||||
|
</p>
|
||||||
|
<div class="float-end">
|
||||||
|
<a href="{{ route('words.index') }}" class="btn btn-primary"> Back</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" action="{{ route('words.update', $id) }}" class="mt-6 space-y-6">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-input-label for="name" :value="__('Italian Word')" />
|
||||||
|
<div class="col-md-6">
|
||||||
|
<x-text-input id="name" name="name" value="{{$name}}" type="text" class="mt-1 block w-full" placeholder="Italian Word" autofocus autocomplete="name"/>
|
||||||
|
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-input-label for="translation" :value="__('Dialect Word')" />
|
||||||
|
<div class="col-md-6">
|
||||||
|
<x-text-input id="translation" name="translation" value="{{$translation}}" type="text" class="mt-1 block w-full" placeholder="Dialectic Word" autofocus autocomplete="translation" />
|
||||||
|
<x-input-error :messages="$errors->get('translation')" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-app-layout>
|
||||||
@@ -1,22 +1,30 @@
|
|||||||
<section>
|
<x-app-layout>
|
||||||
<header>
|
<div class="card">
|
||||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
<div class="card-header">
|
||||||
{{ __('Upload CSV File ') }}
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||||
</h2>
|
{{ __('Upload CSV File ') }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||||
{{ __("Upload file to add group of word in db") }}
|
{{ __("Upload file to add group of word in db") }}
|
||||||
</p>
|
</p>
|
||||||
</header>
|
<div class="float-end">
|
||||||
|
<a href="{{ route('words.index') }}" class="btn btn-primary"> Back</a>
|
||||||
<form action="{{ route('store') }}" method="post" class="mt-6 space-y-6" enctype="multipart/form-data">
|
</div>
|
||||||
@csrf
|
|
||||||
|
|
||||||
<x-text-input type="file" id="filetoinsert" name="filetoinsert"/>
|
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('filetoinsert')" />
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<br>
|
|
||||||
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</section>
|
<div class="card-body">
|
||||||
|
<form action="{{ route('words.uploadFile') }}" method="post" class="mt-6 space-y-6" enctype="multipart/form-data">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-text-input type="file" id="filetoinsert" name="filetoinsert"/>
|
||||||
|
<x-input-error class="mt-2" :messages="$errors->get('filetoinsert')" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-app-layout>
|
||||||
|
|||||||
@@ -62,12 +62,14 @@ Route::prefix('words')->group(function () {
|
|||||||
Route::post('/', [WordsController::class, 'store'])->name('words.insert');
|
Route::post('/', [WordsController::class, 'store'])->name('words.insert');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::get('insert_word', [WordsController::class, 'insert'])->name('words.edit');
|
||||||
Route::get('delete_record/{id}', [WordsController::class, 'delete']);
|
Route::get('delete_record/{id}', [WordsController::class, 'delete']);
|
||||||
Route::get('edit_record/{id}', [WordsController::class, 'edit']);
|
Route::get('edit_record/{id}', [WordsController::class, 'edit']);
|
||||||
Route::post('update_record/{id}', [WordsController::class, 'update'])->name('words.update');;
|
Route::post('update_record/{id}', [WordsController::class, 'update'])->name('words.update');;
|
||||||
|
|
||||||
//***** FILE *****\\
|
//***** FILE *****\\
|
||||||
Route::post('datasubmit', [FileController::class, 'datasubmit'])->name("store");
|
Route::get('insert_file', [FileController::class, 'insertFile'])->name('words.insertFile');
|
||||||
|
Route::post('upload_file', [FileController::class, 'uploadFile'])->name('words.uploadFile');
|
||||||
|
|
||||||
Route::resources([
|
Route::resources([
|
||||||
'roles' => RolesController::class,
|
'roles' => RolesController::class,
|
||||||
|
|||||||
Reference in New Issue
Block a user