ساخت پنل ادمین با لاراول به سریع ترین روش ممکن

از کجا شروع شد؟
تنبلی آقا تنبلی! تنبلی سرچشمه همه این چیزایی هست که میخام براتون بنویسم.
تنبلی که نتیجه ش الان ساخت این پکیج هست که کارم رو حسابی سریع کرده :) .

واسه همه پروژه هایی که دستم میرسید بلاخره باید یه پنل ادمین میساختم میدادم دست صاحبش تا بتونه اونجوری که میخاد سایتش رو پر کنه. روند ساخت ادمین هم که مشخص بود.

https://virgool.io/Software/%D8%B3%D8%A7%D8%AE%D8%AA-crud-%D8%A8%D8%A7-%D9%84%D8%A7%D8%B1%D9%88%D8%A7%D9%84-%D9%88-vue-oa3s5ageyafv

البته اون اوایل با vue نمیزدم :).

اولین پنل ادمین من با لاراول خیلی هم داغون
اولین پنل ادمین من با لاراول خیلی هم داغون

دقیقا اولین سایتی که پنل ادمین براش زدم خیلی سنگین بود و بخش های خیلی مختلفی داشت
از همه این ها بدتر اینکه چند زبانه بود.
یعنی اصلا اینجوری بگم صاحب کار اولم کلا هر سایتی از هر مشتری میگرفت به من میگفت چند زبانه باشه و زبان ها هم به صورت داینامیک اضافه بشه از طریق خود پنل ادمین !
من هر قسمت رو که میزدم باید کلی کد مینوشتم.
برای هر قسمت یه
view
کاملا جدا میزدم. حتی فرم افزودن و فرم اصلاح رو هم جدا از هم درست میکردم و یه تنه قائده DRY رو زیر سوال برده بودم ?. پیشنهاد میکنم در مورد این قائده بخونین و سعی کنین ازش پیروی کنین.

https://en.wikipedia.org/wiki/Don%27t_repeat_yourself

نتیجه کار این بود که برای هر اصلاح کوچیک بخش های خیلی زیادی رو باید تغییر میدادم.
یه چیز دیگه این بود که یه بخش هایی از کار رو کپی پیست میکردم مثلا برای
dropzone
باید کلی کد مختلف مینوشتم و دقیقا تو هر کنترلر و ویو کلی کد تکراری مینوشتم و کپی پیست میکردم و اگه جایی نیاز به اصلاح داشت باید یه عالمه بخش های مختلف رو اصلاح میکردم.
من حتی با قابلیت fill , fillable و اینا آشنایی نداشتم و هر فیلد رو اینجوری پر میکردم!

$post->test_1 = $request->input(&quottest1&quot);
$post->test_2 = $request->input(&quottest 2&quot);

و هر بار ادیت کردن یه بخش یه عذاب حسابی واسه من بودش.
حالا یه پروژه جدید میومد و من مجبور بودم هی کد کپی کنم از این پروژه ببرم تو اون پروژه, اصن یه وضی.

خب حالا از کجا روش سریع تر به ذهنم رسید.
همکار من یه پکیج به اسم voyager به من معرفی کرد.

https://laravelvoyager.com/

من این پکیج رو امتحان کردم و واقعا ازش لذت بردم. برای شروع و تست کردنش یه پنل یه وبلاگ رو یه روزه پیاده سازی کردم باهاش کاری که راحت از من یه هفته وقت میگرفت و خب یه خط کد هم ننوشتم!!!
خیلی خوب بود به خودم میگفتم ایول من همین رو اصن استفاده میکنم از این به بعد کی حال داره ادمین بزنه.
اگه متن رو از اول دقیق خونده باشین میدونین اینجا جاییه که مشکل میخورم.
یه مدیر که نه تنها سایت چند زبانه میخاد بلکه میخاد زبان ها از طریق ادمین داینامیک اضافه بشن و حذف بشن. منم جا نزدم گفتم احتمالا باید پکیج دیگه ای هم باشه که بتونه این کار رو برام انجام بده( ولی نبود ).

اون پکیج life changing چی بود؟!

خب یه پکیج پیدا کردم که بهم فهموند یه چیزی مثل
voyager

چجوری کار میکنه

http://crudbooster.com/

این پکیج بر خلاف
voyager
نیاز به کد نویسی داشت و این همون چیزی بود که نیاز داشتم تا بفهمم چجوری میشه یه ادمین رو سریع تر پیاده سازی کرد.

$this->col = []; $this->col[] = [&quotlabel&quot=>&quotName&quot,&quotname&quot=>&quotname&quot];
$this->col[] = [&quotlabel&quot=>&quotHomeroom Teacher&quot,&quotname&quot=>&quothomeroom_teacher&quot]; 

$this->form[] = [&quotlabel&quot=>&quotName&quot,&quotname&quot=>&quotname&quot,&quottype&quot=>&quottext&quot,&quotrequired&quot=>TRUE,&quotvalidation&quot=>&quotrequired|string|min:3|max:70&quot,&quotplaceholder&quot=>&quotYou can only enter the letter only&quot];
$this->form[] = [&quotlabel&quot=>&quotHomeroom Teacher&quot,&quotname&quot=>&quothomeroom_teacher&quot,&quottype&quot=>&quottext&quot,&quotrequired&quot=>TRUE,&quotvalidation&quot=>&quotrequired|min:1|max:255&quot];

خب این پکیج اومده بود یه کنترلر ساخته بود و مشخصات فیلد های جدول و فیلد های فرم ها رو تو آرایه ذخیره کرده بود و با استفاده از همین ها ویو های مناسب رو تولید میکرد. و بعد موقع ذخیره دوباره با استفاده از همین ها تشخیص میداد هر فیلد رو چجوری ذخیره کنه.
با همین تکنیک سعی کردم پنل ادمین خودم رو دقیقا با همین سبک بسازم. اینطوری هم لازم نبود مثل قدیم هی کد اینور اونور کپی پیست کنم. هم قابلیت زبان رو هم به صورت داینامیک داخل خود ادمین اضافه میکردم.

ولی خب من یه تفاوتی بین ادمین خودم و crudbooster گذاشتم. اونم این بود که crudbooster یه آرایه برای فیلد فرم ها داشت و یکی دیگه برای جدول, من هر دو این ها رو ادغام کردم و اسمشون رو گذاشتم
widget!

خروجی این یه فرم و جدول کامل به اصطلاح CRUD بود که فقط با داشتن مدل کار خودش رو انجام میداد. فقط با همین میزان کد برای هر پروژه بعدی که به دستم میرسید. بدون نیاز به ساخت ویو جدا یا کد ذخیره سازی جدا کارش رو انجام میداد.

خب حالا میخام یه توضیح سطحی بدم از اینکه خودتون چجوری میتونین یه چیز شبیه این رو درست کنین.
اولین قدم ساخت جدول هست. شما برای ساخت جدول به یه تعداد اطلاعات نیاز دارین.
اینکه جدولتون چه ستون هایی داره و اینکه چه چیز هایی تو هر سطر و ستون نشون بده.
خب برای این کار نیاز به دو تا آرایه (یا کالکشن نیاز دارین).
یه آرایه که لیست فیلد ها رو نگه داره.

$fields = [    
    ['name' => 'name', 'title' => 'نام'],
    ['name' => 'email' => 'title' => 'ایمیل']
]

و خب لیست کاربران.

$users = User::all();

کد ویو جدول یه چیز شبیه این میشه. کد رو ذهنی مینویسم.

<table>
    <thead>
        @foreach($fields as $field)
            <th>{{ $field['title'] }}</th>
        @endforeach
    </thead>
    <tbody>
        @foreach($users as $user)
             <tr>
            @foreach($fields as $field)
                <td>
                   {{ $user->{ $field['name'] } }}
                </td>
             @endforeach
             </tr>
          @endforeach
    </tbody>
</table>

این جدول رو تشکیل میده اما فیلد های فرم یکمی از این پیچیده تر هستن.
شما ممکنه نوع ورودی مختلف داشته باشین
مثل چک باکس, متن, رمز عبور, فایل
هر کدوم از این ها هم به سبک خودشون باید تو دیتابیس ذخیره بشن.
پس تو لیست فیلد ها به نوع هم احتیاج هست.

$fields = [
    ['name' => 'name',  'title' => 'نام', 'type' => 'text'],
    ['name' => 'email', 'title' => 'ایمیل', 'type' => 'email'],
    ['name' => 'password', 'title' => 'رمز عبور', 'type' => 'password']
];

حالا برای ویو فرم.

@foreach($fields as $field)
    <input type=&quot{{ $field['type'] }}&quot id=&quot{{ $field['name'] }}&quot name=&quot{{ $field['name'] }}&quot />
     <label for=&quot{{ $field['name'] }}>{{ $field['name'] }}</label>
@endforeach

برای ذخیره سازی این مثال دو حالت داریم.
برای رمز عبور باید از تابع bcrypt استفاده بشه و بقیه فیلد ها ذخیره عادی کافی هست.

foreach ($fields as $field) {
    if ($field['type'] == 'password') {
        $model->{ $field['name'] } = bcrypt($request->input($field['name']));
    } else {
        $model->{ $field['name'] } = $request->input($field['name']);
    }
}

این یه ادمین دیتامحور خیلی ساده هست. دو تا فیلد که نحوه ذخیره شون با هم فرق داره بیشتر نداره.
اگه بخواین همه رو با if و else ذخیره کنین فرض کنین 20 نوع فیلد مختلف وجود داشته باشه. اون وقته که کد حسابی شلوغ میشه. این مشکل رو من با تبدیل کردن این آرایه ها به کلاس حل کردم به این شکل که نحوه ذخیره کردن و کد نمایش فیلد و ... همه درون همون کلاس نگه داری میشن و دیگه نیازی به دست بردن تو کد کنترلر یا ویو CRUD نیست. یه تابع تو کلاس هر widget برای render کردن وجود داره و من دیگه نیازی به این ندارم که دستی چیزی رو چک کنم.

@foreach($fields as $field)
    {!! $field->getHtml() !!}
@endforeach

همچنین برای ذخیره سازی دیگه نیازی به if و else ندارم.

foreach ($fields as $field) {
    $field->store($request, $model);
}

برای مثال برای همین ویجت رمز عبور چنین تابعی باید داشته باشم.

public function store(Request $request, $model) {
    $model->{ $this->name } = bcrypt($request->input($this->name));
}

همچنین برای اون قسمت getHtml کافیه یه view بنویسم تا کارم رو راه بندازه.

public function getHtml() {
    return view('admin.components.password_field');
}

و به این ترتیب کد مخصوص فیلد رو میفرستم به ویو اصلی crud بدون اینکه نیاز باشه به کد
crud
دسترسی داشته باشم یا نیاز باشه که اصلاحش کنم.
گاهی اوقات پیش میاد که کد html خالی کافی نیست. برای مثال همون dropzone برای درست شدنش علاوه بر html به کد های جاوا اسکریپت هم نیاز داریم. بنابراین دقیقا زیر همون صفحه فیلد یه تگ script هم لازم داریم که کد جاوا اسکریپت فیلد ها در اونجا استفاده میشن.


    @foreach($fields as $field)
        {!! $field->getJs() !!}
    @endforeach

به همین راحتی میتونین جاوا اسکریپت رو هندل کنین.
خب اگه بخام همینجوری ادامه بدم باید یه کتاب بنویسم در مورد کارایی هایی که میتونین به ادمینتون اضافه کنین. شما باید بر اساس نیازتون یه کارایی به ادمینتون اضافه کنین. یعنی تا نیاز حس نشه نمیتونین کاری کنین.
بخام مثال بزنم همین فیلد نوع فایل مثلا شما باید تشخیص بدین که مدل تون حذف شده یا تغییر پیدا کرده که بتونین خود فایل رو از پوشه آپلود ها پاک کنین تا فضای هاست اشغال نشه. نوشتن این کد با ایونت مدل ها خیلی راحت هست. فقط کافیه تو کنترلری که crud رو هندل میکنه یه قسمت اضافه کنین تا ایونت مدل ها رو به تابعی تو کلاس فیلد هاتون بفرسته.

ماژول کردن این پنل

به شدت پیشنهاد میکنم ادمینی که میخاین بسازین رو به شکل یه ماژول در بیارین

چرا؟
فرض کنین سه تا سایت مختلف با این کد ادمینتون میسازین. یهو سر چهارمین پروژه متوجه میشین که یه اشتباهی یه جا انجام دادین. خب روی چهارمین سایت درستش میکنین ولی انقدر مهم هستش که مجبور میشین رو سایت قبلی ها هم همین کار رو انجام بدین. چون کد ها کپی پیست شدن از هر پروژه به پروژه دیگه نیاز دارین که برگردین و تک تک پروژه ها دونه به دونه درست کنین. ولی اگه ماژول کنین میتونین خیلی راحت فقط روی چهارمین پروژه درستش کنین و از بقیه پروژه ها تغییرات رو با سابماژول گیت یا کمپوزر دریافت کنین خیلی راحت, شیک و مجلسی.
برای ماژول کردن پنل ادمین دوتا راه پیشنهاد میدم.
اول اینکه سعی کنین پکیج نویسی رو در لاراول یاد بگیرین و خودتون پکیج بسازین. البته سوء برداشت نشه که شما اگه پکیج بسازین مجبور هستین که اون رو عمومی منتشر کنین خیلی راحت میتونین با قابلیت
repositories کمپوزر پکیج خودتون رو خصوصی نگه دارین و تو تیم خودتون ازش استفاده کنین.

https://getcomposer.org/doc/05-repositories.md#git-alternatives

مستندات پکیج نویسی لاراول:

https://laravel.com/docs/5.8/packages

خب اما یه راه دوم هم برای ماژول نویسی هست و اونم این پکیج هست.

https://github.com/nWidart/laravel-modules

من شخصا از این استفاده نکردم تا حالا اما دیدم کسایی رو که استفاده میکنن!
خب امیدوارم تونسته باشم یه دید اولیه از پنل ادمین دیتامحور و اینکه چجوری یکی برای خودتون یا تیم تون بسازین بهتون داده باشم. حداقل برای یک بار هم که شده این روش رو امتحان کنین شاید خوشتون اومد به خصوص تیم های کوچیک (مثل ما ?) که کارش زدن پروژه های کوچیک هست و الویتش کمیت پروژه ها هست.

یه کم اضافه تر!

میخام یه تعداد از پنل هایی که این سبک کار میکنن رو بهتون معرفی کنم. حالا به اسم CMS یا CMF یا admin generator یا هرچیز دیگه ای.

اولین چیزی که میخام معرفی کنم, نه با لاراول ساخته شده نه اینکه ادمین ساز هست بلکه یه سیستم مدیریت محتوا کامله که دیتا رو از روی یه فایل yaml میخونه و با توجه به اون نه تنها ادمین میسازه بلکه دیتابیس و صفحات وبسایت رو هم میسازه. اسم این سیستم مدیریت محتوا bolt هست.

https://bolt.cm/

امکانات جالبی داره و پیشنهاد میکنم کد نویسیش رو بررسی کنین براتون خیلی جالب خواهد بود و دید بازتری به این مسائل بهتون میده.

دومین چیزی که میخام معرفی کنم پکیج orchid هست. این پکیج هم به همین سبک کار میکنه.

https://github.com/orchidsoftware/platform

این پکیج هم با کلاس بندی کردن فیلد ها سورسش میتونه دید بازی بهتون بده.

سومین پکیج , پکیج laravel-admin رو معرفی میکنم که با همین سبک کار میکنه.

https://laravel-admin.org/

چهارمین پکیج litstack.

https://github.com/litstack/litstack

پکیج بعدی که معرفی میکنم بهتون اسمش filament هست
خوبی این پکیج این هست که با
livewire
نوشته شده و به راحتی میشه هر چیزیش رو شخصی سازی کرد و توسعه و کامیونیتی‌ش هم در حال حاضر به شدت فعاله.

https://filamentphp.com/

Last but not least!

آخرین ولی مهم ترین هم پکیج نوا هست که توسط سازندگان خود لاراول ساخته شده و این هم با همین سبک کار میکنه.

https://nova.laravel.com/

تنها بدی این پنل هم اینه که پولی هست و هزینه ش به ریال واقعا خیلی سنگینه و نمیشه مشتری رو به پرداخت هزینه ش متقاعد کرد.

سخن آخر

ولی باز هم پیشنهاد اول من بهتون اینه که برای تیم خودتون چنین چیزی رو پیاده سازی کنین. اگه هم خواستین از پکیج های آماده ای که معرفی کردم استفاده کنین قبل از هرکاری مطمئن بشین به پکیج مورد نظرتون تسلط کامل دارین تا جایی به مشکل نخورین بعد پروژه های واقعی رو با اون ها بسازین.
درسته که ساختن چنین چیزی از پایه زمان میبره اما مطمئن باشین در پروژه های متعدد باعث میشه حسابی در زمان صرفه جویی کنین و این زمان گذاشتن ارزشش رو داره.


سایر نوشته هام.

https://virgool.io/@amiralizadeh9480/%D9%88%D8%B5%DB%8C%D8%AA-%D9%86%D8%A7%D9%85%D9%87-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-dlyu27r2ijld
https://virgool.io/@amiralizadeh9480/%D8%A7%D9%81%D8%B2%D9%88%D9%86%D9%87-%D9%85%D9%86-%D8%A8%D8%B1%D8%A7%DB%8C-vscode-%D9%88-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-mw9bcpvidbrd
https://virgool.io/@amiralizadeh9480/%DA%86%DB%8C-%D8%B4%D8%AF-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3-%D8%B4%D8%AF%D9%85-kpsescdv5rci