ببعض الأحيان قد. نظطر للقيام بعملية بحث في أكثر من Eloquent models مثل ( posts, authors, books )، وواحده من هذه الحلول هي إنشاء جملة query لكل model.
لنفرض أن لدينا الجداول التالية posts, authors, books
ولدينا form واحد في ملف blade ومن خلال هذا الفورم يتم البحث في جميع الجداول أعلاه
ما هي الحلول المتاحة أمامنا، بداية سوف أبدأ من أبسط فكرة.
تعتبر هذه الطريقة الأسل لأداء المهمة
public function index(Request $request) { $results['posts'] = Post::where('title', 'like', '%' . $request->search . '%')->get(); $results['authors'] = Author::where('author_name', 'like', '%' . $request->search . '%')->get(); $results['books'] = Book::where('book_name', 'like', '%' . $request->search . '%')->get(); return view('articles', compact('results')); }
وفي ملف blade يمكن عمل foreach وتحقق من نتائج كل query
<div class="font-bold mt-2 mb-2">Posts:</div> @if ($results['posts']->count()) <ul class="list-inside"> @foreach ($results['posts'] as $post) <li class="list-disc">{{ $post->title }}</li> @endforeach </ul> @else <span class="text-danger">No results.</span> @endif <div class="font-bold mt-2 mb-2">Authors:</div> @if ($results['authors']->count()) <ul class="list-inside"> @foreach ($results['authors'] as $author) <li class="list-disc">{{ $author->author_name }}</li> @endforeach </ul> @else <span class="text-danger">No results.</span> @endif <div class="font-bold mt-2 mb-2">Books:</div> @if ($results['books']->count()) <ul class="list-inside"> @foreach ($results['books'] as $book) <li class="list-disc">{{ $book->book_name }}</li> @endforeach </ul> @else <span class="text-danger">No results.</span> @endif
تعتبر الطريقة أعلاه صحيحة، وتؤدي الغرض بشكل سليم، لكنها تعتبر طويلة من حيث جمل eloquent وكذلك التحقق و foreach في ملف الـ blade.
حيث تساعدنا هذه الحزمة في إستخدام جملة eloquent واحده، وكذلك foreach واحده في ملف blade
بداية تثبيت الحزمة
composer require spatie/laravel-searchable
في الموديل نقوم بعمل implement للـ Searchable interface ومن ثم إضافة الدالة getSearchResult لتحديد الـ field الذي سيتم البحث به.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Searchable\Searchable; use Spatie\Searchable\SearchResult; class Post extends Model implements Searchable { public function getSearchResult(): SearchResult { return new \Spatie\Searchable\SearchResult( $this, $this->title, ); } }
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Searchable\Searchable; use Spatie\Searchable\SearchResult; class Author extends Model implements Searchable { public function getSearchResult(): SearchResult { return new \Spatie\Searchable\SearchResult( $this, $this->author_name, ); } }
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Searchable\Searchable; use Spatie\Searchable\SearchResult; class Book extends Model implements Searchable { public function getSearchResult(): SearchResult { return new \Spatie\Searchable\SearchResult( $this, $this->book_name, ); } }
ومن ثم في الـ Controller في دالة البحث يتم أخذ new object من search ونستخدم registerModel
use Spatie\Searchable\Search; public function index(Request $request) { $results = (new Search()) ->registerModel(Post::class, 'title') ->registerModel(Author::class, 'author_name') ->registerModel(Book::class, 'book_name') ->search(request('search')); return view('articles', compact('results')); }
الأن نأتي للجزء الأهم وهو ملف blade حيث إنه بشكل تلقائي يتم عمل group للبيانات
@forelse($results->groupByType() as $type => $modelSearchResults) <div class="font-bold mt-2 mb-2">{{ ucfirst($type) }}</div> <ul class="list-inside"> @foreach($modelSearchResults as $searchResult) <li class="list-disc"> {{ $searchResult->title }} </li> @endforeach </ul> @empty No results. @endforelse
وكما نلاحظ إنه في ملف blade تم إستخدام foreach واحده، عوضاً عن إستخدام ثلاثة foreach في الطريقة الأولى.
كما يمكن أيضا تمرير أكثر من attribute للبحث
$searchResults = (new Search()) ->registerModel(User::class, 'first_name', 'last_name') ->search('john');
الأن السؤال : إذا كان هناك نتائج تتعلق بـ Post Model يجب أن يتم توجيه المستخدم عند الضغط على النتيجة إلى post/id وإذا كانت النتائج تتعلق بـ Book Model يجب أن يتم توجيه المستخدم إلى book/id وهكذا، كيف ممكن عمل ذلك؟.
بداية نحتاج لإضافة الخاصية url في الدالة getResult التي تم إنشاؤها في كل موديل
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Searchable\Searchable; use Spatie\Searchable\SearchResult; class Post extends Model implements Searchable { public function getSearchResult(): SearchResult { return new \Spatie\Searchable\SearchResult( $this, $this->title, '/post/'.$this->id ); } }
كما نلاحظ إنه تم إضافة post/$this->id وهذا سيكون مسار عند الضغط على إي نتيجة تتعلق بالـ Post Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Searchable\Searchable; use Spatie\Searchable\SearchResult; class Author extends Model implements Searchable { public function getSearchResult(): SearchResult { return new \Spatie\Searchable\SearchResult( $this, $this->author_name, '/author/'.$this->id ); } }
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Searchable\Searchable; use Spatie\Searchable\SearchResult; class Book extends Model implements Searchable { public function getSearchResult(): SearchResult { return new \Spatie\Searchable\SearchResult( $this, $this->book_name, '/book/'.$this->id ); } }
وفي ملف blade يجب إضافة
<a href="{{ $searchResult->url }}">{{ $searchResult->title }}</a>
@forelse($results->groupByType() as $type => $modelSearchResults) <div class="font-bold mt-2 mb-2">{{ ucfirst($type) }}</div> <ul class="list-inside"> @foreach($modelSearchResults as $searchResult) <li class="list-disc"> <a href="{{ $searchResult->url }}">{{ $searchResult->title }}</a> </li> @endforeach </ul> @empty No results. @endforelse
كلام جميل ومختصر و كليين كوود بس انت هنا في البلييد قرات ال title الذي هوو attribute في post model {{ $searchResult->title }} كيف ممكن اقراء داتا من الموديلات الاخري مع العلم ان ال foreach واحدة
شكراً لك مقالة مفيدة
Muna
Can it used for api