php artisan make:middleware MiddleWareTest
فایلی با نام MiddleWareTest.php در آدرس app/Http/Middleware ایجاد شده است.
توجه: برای این پروژه نیازی به اجرای دستور بالا ندارید و فقط جهت آموزش است.
برای اینکه تمامی آدرسهای مربوط به پنل مدیریت نیاز به ورود داشته باشند از Middlewareهای آمادهی لاراول استفاده میکنیم.
حال برای محافظت از آدرسهای پنل مدیریت و ایجاد نیاز به ورود برای دسترسی ما از Middlewareهای آمادهی لاراول جهت اینکار استفاده میکنیم.
از Middleware هم در Routeها و هم در controllerها، میتوانیم استفاده کنیم. در اینجا ما بر روی آدرسهای پنل مدیریت که در قسمتهای قبل آنها را گروهبندی کردیم یک Middleware قرار میدهیم.نحوهی قرار دادن Middleware روی یک آدرس به صورت زیر خواهد بود.
Route::middleware('middleware-class-name')->get('/', 'TestController@index')->name('someName');
برای آدرسهای پنل مدیریت که گروهبندی کردهایم نیز به صورت زیر خواهد بود. در اینجا تمامی خصوصیات آدرسها در اولین ورودی تابع group قرار خواهند گرفت.
Route::group(['prefix' => 'admin', 'namespace' => 'Admin', 'middleware'=>'auth'], function () {
Route::get('/dashboard', 'DashboardController@index')->name('admin');
Route::group(['prefix' => 'portfolios'], function (){
Route::get('/', 'PortfolioController@portfolios')->name('viewListPortfolios');
Route::get('/add', 'PortfolioController@view_add')->name('viewAddPortfolio');
Route::post('/add', 'PortfolioController@add')->name('addPortfolio');
});
});
مقدار auth که برای Middleware گروه ادمین انتخاب کردیم، از طرف خود لاراول تعریف شده است که login بودن کاربر را بررسی میکند.
حالا اگر آدرس اضافه کردن نمونه کار را بدون اینکه وارد شده باشید بزنید به صفحهی ورود منتقل خواهید شد.
<li><a href="{{ route('admin') }}">Dashboard</a></li>
<li><a href="{{ route('viewListPortfolios') }}">Portfolios</a></li>
حالا با لینکهای موجود در سایت میتوانید بین صفحات لیست نمونه کارها و داشبورد جابجا شوید :)
Route::get('/edit/{portfolio}', 'PortfolioController@view_edit')->name('viewEditPortfolio');
Route::post('/edit/{portfolio}', 'PortfolioController@edit')->name('editPortfolio');
@extends('admin.layouts.panel')
@section('content')
<div class="card">
<div class="card-header">Edit Portfolio</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
@if (count($errors))
<div class="alert alert-danger" role="alert">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('editPortfolio', ['portfolio' => $portfolio]) }}" method="post" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" value="{{ $portfolio->title }}" id="title" name="title" aria-describedby="titleHelp">
<small id="titleHelp" class="form-text text-muted">Choose a title for your portfolio</small>
</div>
<div class="custom-file">
<input type="file" class="custom-file-input" id="image" name="image">
<label class="custom-file-label" for="image">Choose image</label>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" id="description" name="description" rows="3">{{ $portfolio->description }}</textarea>
</div>
<div class="form-group">
<label for="rank">Rank</label>
<select class="custom-select" id="rank" name="rank">
<option value="1" @if($portfolio->rank == 1) {{'selected'}} @endif>1</option>
<option value="2" @if($portfolio->rank == 2) {{'selected'}} @endif>2</option>
<option value="3" @if($portfolio->rank == 3) {{'selected'}} @endif>3</option>
<option value="4" @if($portfolio->rank == 4) {{'selected'}} @endif>4</option>
<option value="5" @if($portfolio->rank == 5) {{'selected'}} @endif>5</option>
</select>
</div>
<div class="form-group">
<label for="client">Client</label>
<input type="text" class="form-control" id="client" value="{{ $portfolio->client }}" name="client">
</div>
<div class="form-group">
<label for="link">Link</label>
<input type="text" class="form-control" id="link" value="{{ $portfolio->link }}" name="link" aria-describedby="linkHelp">
<small id="linkHelp" class="form-text text-muted">Enter project link like:
https://7learn.com</small>
</div>
<div class="form-group">
<label for="completion_date">Completion Date</label>
<input type="text" class="form-control" id="completion_date" value="{{ jdate($portfolio->completion_date)->format('Y/m/d') }}" name="completion_date"
aria-describedby="completion_dateHelp">
<small id="completion_dateHelp" class="form-text text-muted">Attention just enter like:
1398/12/02</small>
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea class="form-control" id="content" name="content" rows="10">{{ $portfolio->content }}</textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
@stop
مقدار متغیر portfolio از سمت controller به فایل view ارسال شده است و ما در این صفحه از آن استفاده کردیم و مقادیر نمونه کار را در فیلدها جهت ویرایش قرار دادیم.
در بالا از تابع route برای آدرس ارسال اطلاعات فرم استفاده کردیم ولی به غیر از نام Route یک ورودی دیگر نیز به آن دادیم. ورودی دوم تابع، یک آرایه میپذیرد که مقادیر پارامترهای Route است. یعنی ما در آدرس ویرایش یک پارامتر به صورت {portfolio} قرار دادیم و در این تابع مقدار این پارامتر را برابر نمونه کار در حال ویرایش آن، قرار دادیم.
public function view_edit(Portfolio $portfolio)
{
return view('admin.portfolios.edit', ['portfolio' => $portfolio]);
}
در ورودی تابع بالا یک مدل از نمونه کار داریم که در پارامتر آدرس مربوطه نیز نوشته بودیم. حال ممکن است سوالی مطرح شود که ما باید کل مدل اعم از تمامی فیلدها و غیره را در آدرس ارسال کنیم؟ قطعا نه، لاراول در نسخهی جدید خود زمانی که ورودی تابع controller را مدل ارسالی قرار میدهیم به صورت خودکار کلید اصلی (Primary Key) را ملاک برای پارامتر آدرس قرار میدهد که در اینجا id کلید اصلی است.
بعد از ارسال نمونه کار در تابع ما به صورت مستقیم نمونه کار را به صفحهی view ارسال میکنیم.
تابع بعد زمانی است که اطلاعات ویرایش شده و به این تابع جهت ثبت تغییرات در دیتابیس ارسال میشود.
public function edit(Request $request, Portfolio $portfolio)
{
$request->validate([
'title' => 'required',
'description' => 'required',
'rank' => ['required', Rule::in(['1', '2', '3', '4', '5'])],
'client' => 'required',
'link' => 'required',
'completion_date' => 'required',
'content' => 'required',
]);
$portfolio->update([
'title' => $request->title,
'description' => $request->description,
'rank' => $request->rank,
'client' => $request->client,
'link' => $request->link,
'completion_date' => Jalalian::fromFormat('Y/m/d', $request->completion_date)->toCarbon(),
'content' => \request('content'),
]);
if ($request->hasFile('image')){
File::delete(public_path().'/portfolios/'.$portfolio->image);
$image = $request->file('image');
$filename = $portfolio->id . '_' . time() . '.' . $image->getClientOriginalExtension();
$image->move('portfolios', $filename);
$portfolio->image = $filename;
$portfolio->save();
}
session()->put('status', 'Portfolio updated successfully !');
return redirect(route('viewListPortfolios'));
}
در قسمت اول از اعتبارسنجی برای اطلاعات ارسالی استفاده کردیم ولی تصویر را در آن قرار ندادیم زیرا تصویر زمانی عوض میشود که ارسال شود. یعنی اگر شما در صفحهی ویرایش نمونه کار تصویری انتخاب نکنید، همان تصویر برای نمونه کار باقی خواهد ماند.
از تابع update برای ویرایش اطلاعات نمونه کار استفاده کردیم و فقط تصویر را در آن قرار ندادیم.
یک شرط برای زمانی که اگر فایلی با کلید image ارسال شده بود قرار دادیم، به این معنی است که تصویر باید تغییر کند. اول تصویر قبلی نمونه کار را حذف میکنیم و سپس طبق توضیحات قسمت قبل تصویر جدید را قرار میدهیم. حذف کردن فایل تصویر نمونه کار فقط برای این انجام میشود که تصویری اضافی روی سرور نباشد و حجم پروژه بالا نرود.
حال زمانی که آدرس https://127.0.0.1:8000/admin/portfolios/edit/1 را باز کنید باید با صفحهی ویرایش نمونه کار مواجه شوید ولی ما نمیتوانیم که برای هربار ویرایش بخواهیم آدرس را دستی بنویسیم. برای همین باید در لیست نمونه کارها یک دکمه قرار دهیم که به صفحهی ویرایش لینک شود.
کد زیر را برای قسمت جدول در صفحه list.blade.php قرار دهید.
<table class="table table-hover">
<thead class="thead-dark">
<tr>
<th scope="col">id</th>
<th width="20px" scope="col">Image</th>
<th scope="col">Title</th>
<th scope="col">Link</th>
<th scope="col">Client</th>
<th scope="col">Created At</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
@foreach($portfolios as $portfolio)
<tr>
<th scope="row">{{ $portfolio->id }}</th>
<td><img src="{{ asset('portfolios/'.$portfolio->image) }}" alt="Portfolio" class="img-fluid"></td>
<td>{{ $portfolio->title }}</td>
<td>{{ $portfolio->link }}</td>
<td>{{ $portfolio->client }}</td>
<td>{{ jdate($portfolio->created_at)->format('Y/m/d') }}</td>
<td>
<a href="{{ route('viewEditPortfolio', ['portfolio' => $portfolio]) }}" class="btn btn-warning">
<svg class="bi bi-pencil-square" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="https://www.w3.org/2000/svg">
<path d="M15.502 1.94a.5.5 0 010 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 01.707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 00-.121.196l-.805 2.414a.25.25 0 00.316.316l2.414-.805a.5.5 0 00.196-.12l6.813-6.814z"/>
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 002.5 15h11a1.5 1.5 0 001.5-1.5v-6a.5.5 0 00-1 0v6a.5.5 0 01-.5.5h-11a.5.5 0 01-.5-.5v-11a.5.5 0 01.5-.5H9a.5.5 0 000-1H2.5A1.5 1.5 0 001 2.5v11z" clip-rule="evenodd"/>
</svg>
</a>
<form action="#" method="POST" style="display: inline-block">
@method('DELETE')
@csrf
<button type="submit" class="btn-danger btn">
<svg class="bi bi-trash" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="https://www.w3.org/2000/svg">
<path d="M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 01-1-1V2a1 1 0 011-1H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1v1zM4.118 4L4 4.059V13a1 1 0 001 1h6a1 1 0 001-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" clip-rule="evenodd"/>
</svg>
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
حال باید در صفحهی لیست نمونه کارها با صفحه زیر مواجه شوید.
<form action="{{ route('deletePortfolio', ['portfolio' => $portfolio]) }}" method="POST" style="display: inline-block">
@method('DELETE')
@csrf
<button type="submit" class="btn-danger btn">
<svg class="bi bi-trash" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="https://www.w3.org/2000/svg">
<path d="M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 01-1-1V2a1 1 0 011-1H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1v1zM4.118 4L4 4.059V13a1 1 0 001 1h6a1 1 0 001-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" clip-rule="evenodd"/>
</svg>
</button>
</form>
فرم بالا را در قسمت لیست نمونه کارها قبلتر قرار داده بودیم و شما فقط قسمت action فرم را که یک Route تعریف کردیم (جلوتر توضیح خواهیم داد)، تغییر دهید.
از method('DELETE)@ استفاده کردیم که یک فیلد پنهان به صورت زیر ایجاد میکند.
<input type="hidden" name="_method" value="DELETE">
این فیلد به لاراول میفهماند که متد ارسالی DELETE است و از Route::delete عملیات را ادامه میدهد.
Route::delete('/delete/{portfolio}', 'PortfolioController@delete')->name('deletePortfolio');
public function delete(Portfolio $portfolio)
{
File::delete(public_path().'/portfolios/'.$portfolio->image);
$portfolio->delete();
session()->put('status', 'Portfolio deleted successfully!!!');
return redirect(route('viewListPortfolios'));
}
در تابع بالا اول با کمک کلاس File تصویر نمونه کار را در مسیر public/portfolios حذف کردیم و بعد از طریق مدل Portfolio و با تابع delete آن را حذف کردیم.
حالا زمانی که دکمهی حذف را در صفحهی لیست نمونه کارها بزنید باید با تصویر زیر روبرو شوید.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Portfolio;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Validation\Rule;
use Morilog\Jalali\Jalalian;
این قسمت مربوط به وارد کردن (Import) کلاسهای استفاده شده در کلاس خودمان است که باید به شکل بالا باشد.
تا این لحظه ما تمامی موارد مربوط به نمونه کارها را بررسی کردیم. حال یک پکیج معرفی میکنیم که راحتتر میتوانید با آنها پیغامهای اضافه شدن، ویرایش شدن و حذف یا هر گونه پیغامی پس از انجام یک عملیات را مدیریت کنید.
session()->put('status', 'Portfolio created successfully !');
در هنگام بازگشت به صفحهی view مورد نظر یک قسمت از کد که به شکل زیر بود اجرا میشد و مقدار status را نمایش میداد.
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
مشکلی که وجود دارد مدت زمان بودن پیغام است و اینکه در تمامی صفحات نیز نشان میدهد. برای مدیریت بهتر ما از یک پکیج به اسم laracasts/flash استفاده میکنیم که راحتتر میتوانید نوع پیغام ( اینکه زرد رنگ یا سبز یا قرمز باشد) را مشخص کنید و همچنین بعد از Refresh دیگر نمایش نخواهد داد.
برای نصب این پکیج دستور زیر را در ترمینال خود اجرا کنید.
composer require laracasts/flash
حالا برای نمایش پیغامها در هر صفحهای که دوست داریم کد زیر را قرار دهید.
@include('flash::message')
کد بالا قسمتی که مربوط به نمایش پیغام در فایلهای view است را وارد (Import) میکند.
حالا در هر قسمت از controller که از sessionاستفاده کردیم، کد زیر را جایگزین کنید.
flash('Portfolio updated successfully !')->success();
میتوانید پیغامهای خودتان را بگذارید و با متد بعد آن که شامل متدهای زیر میباشد میتوانید نوع پیغام و رنگ باکس پیغام را انتخاب کنید.
[lineltr]
سلام من چک کردم قسمت 4 نبود و قسمت 1 اگه امکان پذیر هست لینک سالم تمام قسمت هارا بدهید و بنده چگونه میتوانم تمام مقالات فقط شما را ببینم باتشکر
درود بله بررسی کردیم... یه دسته از مقالات در حال حذف یا تجمیع هستند و از شانس شما این آموزش هم جزو هموناست :) در پلتفرم فعلی این امکان رو نداریم که بتونید مقالات یک نویسنده خاص رو ببینید اما فیدبکتون رو به تیم انتقال میدم انشالله که اضافه بشه. ممنون که با ما همراه هستید.
دوره الفبای برنامه نویسی با هدف انتخاب زبان برنامه نویسی مناسب برای شما و پاسخگویی به سوالات متداول در شروع یادگیری موقتا رایگان شد: