M. Niyazi Alpay
M. Niyazi Alpay
M. Niyazi Alpay

Çok küçük yaştan itibaren bilgisayar sistemleriyle ilgileniyorum ve 2005 yılından beri programlama ile uğraşıyorum, PHP, MySQL, Python, MongoDB ve Linux konularında bilgi sahibiyim

 

about.me/Cryptograph

  • admin@niyazi.org

Meilisearch ve Laravel Entegrasyonu

Meilisearch ve Laravel Entegrasyonu

Bu yazımca MeiliSearch ve Laravel Entegrasyonundan bahsedeceğim.

MeiliSearch, Elasticsearch ile aynı mantıkla çalışan bir arama motorudur. Ancak Elasticsearch çok fazla kaynak harcamaktadır. MeiliSearch ise daha az kaynak ile benzer işleri yapabilmektedir. Şimdi burada yanlış anlaşılmak istemem. MeiliSearch Elasticsarch’ten daha iyidir şeklinde anlaşılmasın, Elasticsearch dağınık bir sunucu yapısıyla kurulabilir, hatta özellik ve kapasite bakımından MeiliSearch’ten daha iyidir. Ancak çok fazla kaynak harcamak istemiyorsanız, daha az kaynakla yalnızca alakalı arama yapmaya yeterli olan bir RESTful arama API’sidir. MeiliSearch için kısacası “fakirler için elasticsearch”  diyebiliriz.

Aslında Meilisearch bize site içi arama motoru yapmamızı sağlayan bir araç diyebiliriz. Yıllar önce "site içi arama motoru yapımı" konusunda doğrudan mysql üzerinde like sorgusu yaparak aramadan bahsetmiştim. Burada daha gelişmiş bir arama işlemi gerçekleştireceğiz.

MeiliSearch Kurulumu

Kurulum işlemini Ubuntu üzerinden anlatacağım. Terminalde aşağıdaki komutları çalıştırarak kurulum işlemlerini gerçekleştirebilirsiniz.

apt update
apt install curl -y
curl -L https://install.meilisearch.com | sh
chmod +x meilisearch
mv ./meilisearch /usr/local/bin/

 

useradd -d /var/lib/meilisearch -b /bin/false -m -r meilisearch
curl https://raw.githubusercontent.com/meilisearch/meilisearch/latest/config.toml > /etc/meilisearch.toml
mkdir /var/lib/meilisearch/data /var/lib/meilisearch/dumps /var/lib/meilisearch/snapshots
chown -R meilisearch:meilisearch /var/lib/meilisearch
chmod 750 /var/lib/meilisearch

 

cat << EOF > /etc/systemd/system/meilisearch.service
[Unit]
Description=Meilisearch
After=systemd-user-sessions.service

[Service]
Type=simple
WorkingDirectory=/var/lib/meilisearch
ExecStart=/usr/local/bin/meilisearch --config-file-path /etc/meilisearch.toml
User=meilisearch
Group=meilisearch

[Install]
WantedBy=multi-user.target
EOF

 

Bu işlemlerden sonra /etc/meilisearch.toml dosyasını açıp aşağıdaki tanımlamaları yapmanız gerekmekte.

env = "development"
master_key = "YOUR_MASTER_KEY "
db_path = "/var/lib/meilisearch/data"
dump_dir = "/var/lib/meilisearch/dumps"
snapshot_dir = "/var/lib/meilisearch/snapshots"

Meilisearch kurulumu artık tamamlandı “systemctl enable meilisearch” komutu ile servisi başlangıçta çalışacak şekilde ayarlayabilirsiniz. Servisi çalıştırmak için “systemctl start meilisearch” komutunu çalıştırabilirsiniz.

Bu servise SSL sertifikası kurmak isterseniz eğer /etc/meilisearch.toml dosyasında aşağıdaki tanımları da düzenlemeniz gerekecektir.

http_addr = "localhost:7700"
ssl_cert_path = "./path/to/certfile"
ssl_key_path = "./path/to/private-key"

Meilisearch artık hazır, şimdi Laravel ile entegrasyon kısmına geçelim.

İlk önce Scout paketini kuruyoruz. Scout laravel modelinde full-text arama yapmamızı sağlayan bir paket.

composer require laravel/scout
php artisan vendor:publish --provider="LaravelScoutScoutServiceProvider"

Scout için driver olarak Meilisearch kullanacağız, bu sebeple meilisearch/meilisearch-php paketinin kurulumunu yapacağız.

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle

.env dosyasına eklememiz gerekenler

SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey

Eğer halihazırda içerisinde veri bulunan bir sisteme bunu sonradan kurduysanız içeriklerin Meilisearch tarafına senkronizasyonu için aşağıdaki komutu çalıştırmalısınız.

php artisan scout:import "App\Models\ModelName"

Meilisearch tarafındaki tüm indexleri silmek için aşağıdaki komutu çalıştırabilirsiniz.

php artisan scout:flush "App\Models\ModelName"

Arama yapmak istediğimiz modele “Laravel\Scout\Searchable” sınıfını çağırmamız gerekiyor. Temelde Searchable sınıfını çağırmamız tüm işlemler için yeterli. Burada her insert, update ve delete işlemlerinde veritabanında yapılan değişiklik aynı zamanda Meilisearch tarafına da senkronize olacak.

Örneklerle açıklamaya devam ediyorum.

Veritabanı yapımızın aşağıdaki gibi olduğunu varsayalım

  • categories
    • id -> primary_key
    • category_name -> varchar
  • products
    • id -> primary_key
    • product_name  -> varchar
    • product_description -> varchar
  • product_categories
    • id -> primary_key
    • product_id -> index
    • category_id -> index
  • attributes
    • id -> primary_key
    • attribute_name -> varchar
  • product_attributes
    • id -> primary_key
    • product_id -> index
    • attribute_value -> varchar

Products modelimiz aşağıdaki gibi olsun. 

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Products extends Model
{
    use HasFactory;
    use Searchable;

    protected $table = 'products';

    protected $fillable = [
        'product_name',
        'product_description',
        'user_id'
    ];

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    public function productAttributes()
    {
        return $this->hasMany(ProductAttributes::class, 'product_id', 'id');
    }

    public function productCategories()
    {
        return $this->hasMany(ProductCategories::class, 'product_id', 'id');
    }

    public function toSearchableArray()
    {
        $array = $this->toArray();
        $array['user'] = $this->user->name;

        $array['product_attributes'] = $this->productAttributes->map(function ($data) {
            return [
                'attribute_name' => $data->attribute->attribute_name,
                'attribute_value' => $data->attribute_value,
            ];
        })->toArray();

        $array['product_categories'] = $this->productCategories->pluck('category.category_name')->toArray();
        return $array;
    }
}

Burada toSearchableArray fonksiyonunda Meilisearch tarafına nelerin gönderileceğini belirliyoruz. Sitede ürün araması yaparken ürünün bir özelliği üzerinden, kategrorisi üzerinden ya da diğer ürün bilgileri üzerinden aynı ürün için arama yapmamızı sağlayacak. Burada mantık şöyle işliyor; biz ürünün veritabanına kaydını ayrı ayrı tablolarda girdik ve modelleri ilişkilendirerek bağladık, eklenilen içeriğe ait tüm bilgileri de Meilisearch tarafına gönderdik. Meilisearch tarafında bir ürün için tüm ürün özellikleri ve kategorileri için bir kayıt oluşturulmuş oldu. Biz arama yaptığımızda bu sorgu Meilisearch tarafında çalıştırılıyor ve bize sonuçlara ait id değerleri dönüyor. Laravel bu id değerlerini doğrudan veritabanında "where in" şartı ile sorgulayarak tüm sonuçları getiriyor. Böylelikle veritabanı tarafında tüm tablolarda arama yaptırmadan hızlı bir şekilde arama işlemini yapabilmiş oluyoruz.

Şimdi controller tarafında arama işlemini nasıl yaptığımıza gelelim.

<?php

namespace App\Http\Controllers;

use App\Models\Products;
use Illuminate\Http\Request;

class ProductsController extends Controller
{
    public function products(Request $request, Products $products)
    {
        $p = $products->search($request->search)->query(function ($query){
            $query->with(['productAttributes', 'productAttributes.attribute', 'productCategories.category', 'user']);
        })->paginate(10);
        echo "<pre>";
        print_r($p);
        echo "</pre>";
    }
}

Modeli yalnızca search() ile birlikte çağırmak yeterli. $request->search boş geliyorsa eğer tüm sonuçları getirmiş olacak.

Buraya where şartlarını da ekleyebiliriz, sıralama da yaptırabiliriz. Ancak Meilisearch indeximizde filterable, searchable ve sortable alanları config/scout.php dosyasında belirlememiz lazım.

'meilisearch' => [
        'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
        'key' => env('MEILISEARCH_KEY'),
        'index-settings' => [
            AppModelsProducts::class => [
                'filterableAttributes'=> [
                    'id',
                    'product_name',
                    'product_description',
                    'user_id',
                    'user',
                    'product_attributes',
                    'product_categories'
                ],

                'searchableAttributes' => [
                    'id',
                    'product_name',
                    'product_description',
                    'user_id',
                    'user',
                    'product_attributes',
                    'product_categories'
                ],

                'sortableAttributes' => ['product_name', 'created_at', 'updated_at'],
            ],
        ],
    ],

Controllerımızdaki sorguyu yeniden düzenleyebiliriz.

$p = $products->search($request->search)->query(function ($query){
            $query->with(['productAttributes', 'productAttributes.attribute', 'productCategories.category', 'user']);
        })->where('user_id', $request->user_id)->orderBy('created_at', 'DESC')->paginate(10);

Burada $request->search ile ürün kategorisi, ürünün özellikleri, başlığı ya da ürünü ekleyen kullanıcının adını gönderebiliriz arama değeri olarak. 

Laravel - Mailisearch ve veritabanı arasındaki sorgu ilişkisini aşağıdaki görsel çok güzel özetlemiş.

Ayrıca örnek proje dosyalarına https://github.com/niyazialpay/LaravelMeilisearchExample adresinden ulaşabilirsiniz.

Bunları da okumak isteyebilirsiniz

Hiç yorum yok

Yorum Bırakın

E-posta adresiniz yayınlanmayacaktır. Zorunlu alanlar * ile işaretlenmiştir