ল্যারাভেলে পূর্ণাঙ্গ CRUD – নোট অ্যাপ

আমরা ইতিমধ্যে জানি CRUD এর পূর্ণরূপ হচ্ছে Create, read, update এবং delete। যে কোন ডাইনাইমিক অ্যাপের মূল ফিচার। আমরা একটা নোট অ্যাপ তৈরি করব, যেখানে নোট তৈরি করা যাবে, নোট ওয়েব সাইটে দেখানো যাবে, যে কোন নোট আপডেট করা যাবে এবং ইচ্ছে করলে ডিলেট করা যাবে। শুরু করা যাক। কিভাবে ল্যারাভেল প্রজেক্ট তৈরি করা যায়, তা নিয়ে বিস্তারিত এই লিঙ্কে পাওয়া যাবে।

লারাভেলে ডিফল্ট ভাবে SQLite ডেটাবেজ ব্যবহার করে। আমরা ডেটাবেজ হিসেবে তাই ব্যবহার করব। আর ফ্রন্টেন্ডের জন্য ব্যবহার করা হয় টেলউইন্ড সিএসএস। আমরা ডিফল্ট টেলউইন্ড সিএসএস ব্যবহার করব।

শুরুতেই প্রজেক্ট নিজ পছন্দের কোড এডিটর বা IDE তে ওপেন করে নেই। এরপর টার্মিনাল বা কমান্ডলাইন থেকে Note মডেল তৈরি করে নেই। মডেলের পাশাপাশি কন্ট্রোলার, মাইগ্রেশন ফাইলও তৈরি করে নিব আমরা। তাই নিচের কমান্ড লিখবঃ

php artisan make:model Note -cmr

নোট ডেটাবেজ স্কিমা

database/migrations/…._create_notes_table.php ফাইল ওপেন করব। ডিফল্ট ভাবে ডেটাবেজ স্কিমায় ID এবং টাইমস্ট্যাপ যোগ থাকে। আমরা আরো দুইটা কলাম যোগ করব। title এবং note:

 Schema::create('notes', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->longText('note');
            $table->timestamps();
        });

ডেটাবেজ স্কিমা তৈরির পর মাইগ্রেশন করে নিব। তাহলে ডেটাবেজে টেবিল তৈরি হবে। ডেটাবেজ সম্পর্কে বিস্তারিত জানা যাবে এই লেখায়। যদি আপনি ভিজুয়াল স্টুডিও কোড ব্যবহার করেন, SQLite Viewer এই এক্সটেনশনটা ব্যবহার করতে পারেন। এটি ব্যবহার করে SQLite ভিজুয়ালি দেখা যাবে।

 php artisan migrate

এখন যদি আমরা database.sqlite ওপেন করে দেখি, দেখব উপরের স্কিমা অনুযায়ী note টেবিল যুক্ত হয়েছে।

মডেল

ইউজার যখন ডেটা ইনপুট দিবে, তখন ওরা চাইলেই যেন ডেটাবেজে যে কোন কিছু ইনপুট দিতে না পারে, তাই মডেলে কি কি ডেটা ফিল করা যাবে, তা বলে দিতে হবে। তার জন্য App>Model>Note.php ওপেন করব। এরপর protected $fillable = [‘title’, ‘note’]; লাইন যুক্ত করে দিব। সম্পূর্ণ ফাইলঃ

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Note extends Model
{
    use HasFactory;
     protected $fillable = ['title', 'note'];
}

এই লাইন যুক্ত না করলে এমন এরর দেখতে পারিঃ Add [title] to fillable property to allow mass assignment on [App\Models\Note].

রুট বা লিঙ্ক গুলো

নতুন নোট তৈরি করার পূর্বে রুট সেট করে নেই। আমরা জানি রিসোর্স কন্ট্রোলার তৈরি করলে অটোমেটিক অনেক গুলো রুট তৈরি করে দেয়। তার জন্য routes > web.php ফাইলে শুধু মাত্র নিচের কোড যুক্ত করলেই হবেঃ

Route::resource('note', NoteController::class);

এখন যদি আমরা example.com/note এ যাই, তাহলে NoteController এর index এ থাকা মেথড কল করবে। ঐখানে যে ভিউ আমরা রিটার্ণ করব, তাই দেখাবে। তেমনি example.com/note/create ভিজিট করলে NoteController.php এর create মেথড কল হবে। এই web.php ফাইলে NoteController ইম্পোর্ট করে নিতে হবেঃ use App\Http\Controllers\NoteController;

ভিউ

resources > views ফোল্ডারের মধ্যে components নামক একটা ফোল্ডার তৈরি করি। ফোল্ডারের এই নামটা গুরুত্বপূর্ণ। এর ভেতর layout.blade.php নামক একটা ফাইল তৈরি করি।

btw, আমি ম্যাটেরিয়াল আইকন থিম ব্যবহার করেছি ভিজুয়াল স্টুডিও কোডে। যার কারণে উপরের মত করে সুন্দর আইকন দেখাচ্ছে ফাইল এবং ফোল্ডারের পাশে। ভিউ ফাইলে নিচের মত করে কোড লিখি। নিচের কোড গুলো মূলত টেলউইন্ড সিএসএস কম্পোনেন্ট – ন্যাভবার থেকে কপি করেছি। সিম্পল রাখার জন্য আমি কিছু কোড বাদ দিয়েছি।

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com/"></script>
</head>
<body>
    <nav class="bg-gray-800">
        <div class="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
          <div class="relative flex h-16 items-center justify-between">

            <div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
              <div class="flex flex-shrink-0 items-center">
                <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
              </div>
              <div class="hidden sm:ml-6 sm:block">
                <div class="flex space-x-4">
                  <a href="/" class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Home</a>
                  <a href="/note" class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">All Notes</a>
                  <a href="/note/create" class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">New Note</a>
                  <a href="#" class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Contact</a>
                </div>
              </div>
          </div>
        </div>
      </nav>

    <main class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">

        {{ $slot }}

    </main>

</body>
</html>

এই লেআউট ফাইলে সব গুলো পেইজে যে কন্টেন্ট থাকবে, যেমন ন্যাভবার, তাই যুক্ত করেছি শুধু। বাকি কন্টেন্ট গুলো আমরা আলাদা আলাদা ফাইলে যুক্ত করব। ঐ ফাইল গুলোতে এই layout.blade.php এক্সটেন্ড করা যাবে। {{ $slot }} ভ্যারিয়েবলে নির্দিষ্ট পেইজের কন্টেন্ট বসবে।

যেমন এখন আমরা welcome.blade.php ফাইল এডিট করে নিচের মত করে লিখিঃ

<x-layout>
    <div class="text-center">
        <h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl">Welcome page</h1>
        <p class="mt-6 text-base leading-7 text-gray-600">Something awesome is coming!</p>
    </div>
</x-layout>

এরপর যদি আমাদের প্রজেক্ট ব্রাউজারে দেখি, তাহলে নিচের মত আউটপুট পাবোঃ

এখানে <x-layout> দিয়ে বলে দিচ্ছি যে components ফোল্ডারের layout.blade.php ফাইলটা ইনক্লুড করতে। আর ঐ ফাইলের {{$slot}} ভ্যারিয়েবলের মধ্যে <x-layout> ট্যাগের ভেতরের অংশ গুলো যুক্ত করতে। সিম্পল, তাই না?

নতুন নোট তৈরি

নতুন নোট তৈরি করার জন্য আমাদের একটা ফরম তৈরি করে নিতে হবে। resource>views ফোল্ডারে একটা ফাইল তৈরি করি create.blade.php নামে।

<x-layout>
    <div class="w-full bg-slate-50 px-5 py-5">
        <form class="space-y-5" action="{{ route('note.store') }}" method="POST">
            @csrf
            <h3>Add new note</h3>

            <input type="text" id="title" name="title"
                class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
                placeholder="Note Title">
                @error('title')
                <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">{{ $message }}</div>
                @enderror

            <textarea id="note" name="note" rows="4"
                class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
                placeholder="Write your thoughts here..."></textarea>

                @error('note')
                <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">{{ $message }}</div>
                @enderror

            <button type="submit"
                class="inline-flex items-center px-5 py-2.5 text-sm font-medium text-center text-white bg-blue-700 rounded-lg focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900 hover:bg-blue-800">
                Save note
            </button>
        </form>
    </div>
</x-layout>

এখানে নোট তৈরি করার জন্য একটা টাইটেল ফিল্ড এবং নোটের জন্য টেক্সট এরিয়া যুক্ত করেছি। যার আউটপুটঃ

যখন ইউজার example.com/note/create ভিজিট করবে, তখন উপরের ফরম দেখবে। তার জন্য NoteController.php ফাইলে create মেথডে উপরের ভিউ রিটার্ণ করতে হবেঃ

 public function create()
    {
        return view('create');
    }

উপরের ফরমে আমরা যখন Save note বাটনে ক্লিক করব, তা কল করবে NoteController এর store মেথড।

NoteController.php ফাইল ওপেন করব এবং store মেথডে নিচের মত করে লিখব ডেটা গুলো ডেটাবেজে সেভ করার জন্যঃ

     public function store(Request $request){
        $data = $request->validate([
            'title' => ['required', 'string'],
            'note' => ['required', 'string']
        ]);

        $note = Note::create($data);

        return to_route('note.show', $note);
    }

আমরা এখন চাইলে নোট তৈরি করতে পারব। এবং নোট ডেটাবেজে স্টোর হবে। আমরা চাইলে ডেটাবেজ ওপেন করে দেখতে পারি।

সিঙ্গেল নোট

উপরে যদিও আমরা নোট সেভ করা শেষে note.show রুটে রিটার্ণ করেছি। note.show তে গেলে এখনো কিছু দেখাবে না। আগে এটা ফিক্স করি। এই রুটে মূলত সিঙ্গেল নোট দেখাবে। সিঙ্গেল নোট দেখানোর জন্য একটা ফাইল তৈরি করে নেই resources > views ফোল্ডারে single.blade.php নামেঃ

<x-layout>
    <div class="w-full bg-slate-50 px-5 py-5">
        <h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"> {{ $note->title }}</h1>
        <p class="mt-6 text-base leading-7 text-gray-600"> {{ $note->note }}</p>

        <div class="flex justify-between mt-10  ">

            <a href="{{ route('note.edit', $note, $note) }}"
                class="inline-flex items-center px-5 py-2.5 text-sm font-medium text-center text-white bg-blue-700 rounded-lg focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900 hover:bg-blue-800">
                Edit </a>

            <form action="{{ route('note.destroy', $note) }}" method="POST">
                @csrf
                @method('DELETE')

                <button type="submit"
                    class="inline-flex items-center px-5 py-2.5 text-sm font-medium text-center text-white bg-red-700 rounded-lg focus:ring-4 focus:ring-blue-200 dark:focus:ring-red-900 hover:bg-red-800">
                    Delete
                </button>
            </form>

        </div>
    </div>
</x-layout>

উপরে সিঙ্গেল নোট দেখানোর পাশাপাশি নোট এডিট করার জন্য এবং নোট ডিলেট করার জন্য দুইটা বাটন তৈরি করেছি। যার কাজ একটু পরেই করব। তার আগে NoteController.php ফাইলের show মেথড নিচের মত করে লিখিঃ

       public function show(Note $note) {
        return view("single", ['note' => $note]);
    }

আমরা অলরেডি দুইটা কাজ করে ফেলেছি। নতুন নোট তৈরি করা এবং একটা সিঙ্গেল নোট দেখানো। এখন যদি একটা নোট তৈরি করি, তাহলে আমাদের ঐ নোট দেখাবে।

সব গুলো নোট দেখানো

এবার আমরা সব নোট লিস্ট দেখাবো। তার জন্য index.blade.php নামে একটা ফাইল তৈরি করে নেইঃ

<x-layout>
    <div class="text-left mr-auto w-full">

        @foreach ($notes as $note)
            <a class="btn btn-primary ms-1" href="{{ route('note.show', $note) }}">
                <div class="p-5 mb-4  bg-slate-50 px-5 py-5 rounded-3">
                    <p class="text-2xl">{{ $note->title }}</p>
                    <p class="text-base leading-7 text-gray-600">{{ Str::words($note->note, 50) }}</p>
                </div>
            </a>
        @endforeach

        {{ $notes->links() }}

    </div>
</x-layout>

এরপর NoteController.php ওপেন করে index মেথড নিচের মত করে লিখিঃ

    public function index(){
        $notes = Note::query()
            ->orderBy('created_at', 'desc')
            ->paginate(10);
        return view("index", ['notes' => $notes]);
    }

এখন আমরা যদি example.com/note ভিজিট করি, তাহলে নোট গুলোর লিস্ট দেখতে পাবো।

নোট আপডেট করা

NoteController.php ফাইলের edit মেথড নিচের মত করে লিখবঃ

    public function edit(Note $note) {
        return view("edit", ['note' => $note]);
    }

সিঙ্গেল নোট থেকে যখন এডিট বাটনে ক্লিক করবে, তখন এই edit ভিউ রিটার্ণ করবে। তার জন্য edit ভিউ তৈরি করে নিতে হবে। নোট তৈরি করা এবং আপডেট করা অলমোস্ট একই। একই ফরম ব্যবহার করেই করা যাবে। তার জন্য edit.blade.php নামে একটা ফাইল তৈরি করে নিব। এরপর নিচের মত করে লিখবঃ

<x-layout>
    <div class="w-full bg-slate-50 px-5 py-5">
  <form  class="space-y-5" action="{{route('note.update', $note)}}" method="POST" >
                @csrf
                @method('PUT')
            <h3>Add new note</h3>

            <input type="text" id="title" name="title"
                class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
                placeholder="Note Title" value="{{$note-> title}}">

            <textarea id="note" name="note" rows="4"
                class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
                placeholder="Write your thoughts here...">{{$note-> note}}</textarea>

            <button type="submit"
                class="inline-flex items-center px-5 py-2.5 text-sm font-medium text-center text-white bg-blue-700 rounded-lg focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900 hover:bg-blue-800">
                Update note
            </button>
        </form>
    </div>
</x-layout>

NoteController.php এর update মেথড নিচের মত করে লিখবঃ

    public function update(Request $request, Note $note)
    {
        $data = $request->validate([
            'title' => ['required', 'string'],
            'note' => ['required', 'string']
        ]);
        $note->Update($data);
        return to_route('note.show', $note);
    }

নোট আমরা এডিট করতে পারলাম।

নোট ডিলেট করা

ডীলেট করা সবচেয়ে সহজ। NoteController.php ফাইল ওপেন করে delete মেথড নিচের মত করে লিখবঃ

    public function destroy(Note $note)
    {
        $note->delete();
        return to_route('note.index', $note);
    }

এর আগেই আমরা single.blade.php ফাইলে ডিলেট বাটন যুক্ত করেছি নিচের মত করেঃ

            <form action="{{ route('note.destroy', $note) }}" method="POST">
                @csrf
                @method('DELETE')

                <button type="submit"
                    class="inline-flex items-center px-5 py-2.5 text-sm font-medium text-center text-white bg-red-700 rounded-lg focus:ring-4 focus:ring-blue-200 dark:focus:ring-red-900 hover:bg-red-800">
                    Delete
                </button>
            </form>

যখন বাটনে ক্লিক করবে, তখন delete রুটে কল হবে এবং নোট ডিলেট করে দিবে। নোট ডিলেট করে দেওয়ার পর আমরা সব গুলো নোটের লিস্ট বা index রুটে রিটার্ণ করেছি।

হয়ে গেলো একটা সিম্পল CRUD অ্যাপ। NoteController.php এর সব গুলো কোড এক সাথেঃ

<?php

namespace App\Http\Controllers;

use App\Http\Requests\UpdateNoteRequest;
use App\Models\Note;
use Illuminate\Http\Request;

class NoteController extends Controller
{

    public function index(){
        $notes = Note::query()
            ->orderBy('created_at', 'desc')
            ->paginate(10);
        return view("index", ['notes' => $notes]);
    }

    public function create(){
        return view('create');
    }


    public function store(Request $request){
        $data = $request->validate([
            'title' => ['required', 'string'],
            'note' => ['required', 'string']
        ]);

        $note = Note::create($data);

        return to_route('note.show', $note);
    }

    public function show(Note $note) {
        return view("single", ['note' => $note]);
    }

    public function edit(Note $note) {
        return view("edit", ['note' => $note]);
    }

    public function update(Request $request, Note $note)
    {
        $data = $request->validate([
            'title' => ['required', 'string'],
            'note' => ['required', 'string']
        ]);
        $note->Update($data);
        return to_route('note.show', $note);
    }

    public function destroy(Note $note)
    {
        $note->delete();
        return to_route('note.index', $note);
    }
}

Leave a Reply