Laravel — Yajra Datatable işlemleri
Bu yazıda sizlere tablo oluştururken bizlere fazlaca kolaylık sağlayan yajra/laravel-datatables kütüphanesinden bahsetmek istiyorum. Bu kütüphane sayesinde karmaşık veritabanı sorgularından, sayfalama işlemlerinden ve verilerin içinde arama zorluğundan kurtuluyoruz.
Öncelikle daha önce yapmadıysanız ve composer’ın yavaş çalışmasından sizde şikayetçiyseniz composer 2.0 versiyonuna geçerek başlayalım. Terminal ekranınızdan aşağıdaki kodu çalıştıralım.
#/bin/bash_> composer self-update
Projemize kütüphaneyi ekleyerek devam edelim. Bu proje için ben laravel 8.x versiyonunu kullanıyor olacağım. Fakat kütüphane laravel 4.2 den beri tam destek veriyor.
Hadi Başlayalım!
composer require yajra/laravel-datatables:^1.5
Projemize kütüphanemizi ekliyoruz.
* Not: Proje üzerinde html, buton vb. eklentileride anlatacağım için tüm paketleri kurdum. Bu kurulum için php ext-zip extension zorunludur. Siz dilerseniz çekirdek sürümü kullanabilirsiniz.
composer require yajra/laravel-datatables-oracle:"~9.0"
Servis bağlantımızı yapalım.
# config/app.php'providers' => [
// ...
Yajra\DataTables\DataTablesServiceProvider::class,
Yajra\DataTables\HtmlServiceProvider::class,
Yajra\DataTables\ButtonsServiceProvider::class,
Yajra\DataTables\EditorServiceProvider::class,],
Ayarlar ve dosyaların aktarılması için “publish” işlemini yapmamız gerekiyor.
php artisan vendor:publish --tag=datatables
php artisan vendor:publish --tag=datatables-buttons
artık kütüphanemizi kullanmaya başlayabiliriz.
Kütüphaneyi kullanmaya başlamadan önce bağımlılıkları daha kolay yönetilmek için “Repository Pattern” kullanıyor olacağım. Öncesinde tasarım desenlerini araştırarak konu hakkında daha detaylı bilgilere sahip olabilirsiniz. Öncelikle klasörlerimizi oluşturalım. Şu şekilde görünüyor olmalı;
|_ App
|__ Example
|--- Business
|---- Abstraction
|---- Concrete
|--- DataAccess
|---- Abstraction
|---- Concrete
Example yerine siz kendiniz bir isimlendirme yapabilirsiniz. Örnek olarak bir Müşteriler listesi yapacağız. Öncelikle demo içerik için model, migration, factory ve seeder oluşturuyoruz.
php artisan make:model Customers -mcsf-m : migration
-c : controller
-s : seeder
-f : factory
Migration dosyası
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->string('phone');
$table->string('slug')->unique();
$table->text('address');
$table->softDeletes();
$table->timestamps();
});
Seeder dosyası
# database/seeders/CustomersSeeder.phpCustomers::factory()->count(500 )->create();# database/seeders/DatabaseSeeder.php$this->call([
CustomersSeeder::class
]);
Factory dosyası
$name = $this->faker->name;
return [
'name' => $name,
'email' => $this->faker->unique()->safeEmail,
'phone' => $this->faker->phoneNumber,
'slug' => Str::slug( $name ),
'address' => $this->faker->address
];
ve ardından .env üzerinde veritabanı bilgilerini girdikten sonra
php artisan migrate --seed || php artisan migrate:fresh --seed
yazarak veritabanımıza 500 tane demo müşteri eklemiş oluruz. Artık tamamen kütüphane ile ilgili işlemleri yapabiliriz.
Öncelikli olarak “Business/Abstraction” altına ICustomersService dosyası oluşturalım sonrasında “Business/Concrete” altına CustomersManager adında dosya oluşturalım.
ICustomerService dosyasını şu şekilde düzenleyelim.
namespace App\Example\Business\Abstraction;
interface ICustomersService
{
public function listForDatatable();
}
CustomersManager dosyasının içini şu şekilde düzenleyelim.
<?php
namespace App\Example\Business\Concrete;
use App\Example\Business\Abstraction\ICustomersService;
class CustomersManager implements ICustomersService
{
public function listForDatatable()
{
// TODO: Implement listForDatatable() method.
}
}
Sonrasında Veri katmanı içinde ICustomersDataService ve CustomersDataManager adında iki adet dosya oluşturalım. İçlerini şu şekilde düzenleyelim.
# CustomersDataService
<?php
namespace App\Example\DataAccess\Abstraction;
interface ICustomersDataService
{
public function listForDatatable();
}# CustomersDataManager<?php
namespace App\Example\DataAccess\Concrete;
use App\Example\DataAccess\Abstraction\ICustomersDataService;
class CustomersDataManager implements ICustomersDataService
{
public function listForDatatable()
{
// TODO: Implement listForDatatable() method.
}
}
şimdi de müşterileri listelediğimiz rotayı belirleyelim.
# routes/web.phpRoute::group(['prefix'=>'musteri'], static function(){
Route::get('liste', 'CustomersController@list')
->name('get.customers.list');
});
Yajra ile ilgili eklemelere artık başlayabiliriz.
Kullanım: php artisan datatables:make CustomersÇıktı: App\DataTables\CustomersDataTable
Öncelikle dosyamız şu şekilde görünüyor olmalı.
<?php
namespace App\DataTables;
use App\Models\Customer;
use Yajra\DataTables\Html\Button;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Html\Editor\Editor;
use Yajra\DataTables\Html\Editor\Fields;
use Yajra\DataTables\Services\DataTable;
class CustomersDataTable extends DataTable
{
/**
* Build DataTable class.
*
* @param mixed $query Results from query() method.
* @return \Yajra\DataTables\DataTableAbstract
*/
public function dataTable($query)
{
return datatables()
->eloquent($query)
->addColumn('action', 'customers.action');
}
/**
* Get query source of dataTable.
*
* @param \App\Models\Customer $model
* @return \Illuminate\Database\Eloquent\Builder
*/
public function query(Customer $model)
{
return $model->newQuery();
}
/**
* Optional method if you want to use html builder.
*
* @return \Yajra\DataTables\Html\Builder
*/
public function html()
{
return $this->builder()
->setTableId('customers-table')
->columns($this->getColumns())
->minifiedAjax()
->dom('Bfrtip')
->orderBy(1)
->buttons(
Button::make('create'),
Button::make('export'),
Button::make('print'),
Button::make('reset'),
Button::make('reload')
);
}
/**
* Get columns.
*
* @return array
*/
protected function getColumns()
{
return [
Column::computed('action')
->exportable(false)
->printable(false)
->width(60)
->addClass('text-center'),
Column::make('id'),
Column::make('add your columns'),
Column::make('created_at'),
Column::make('updated_at'),
];
}
/**
* Get filename for export.
*
* @return string
*/
protected function filename()
{
return 'Customers_' . date('YmdHis');
}
}
Ben “Server-side Rendering” yapacağım için Ajax ile bağlantıyı özetliyeceğim. Öncelikle bazı düzenlemeler yapmamız gerekecek. Dosyayı şu şekilde düzenliyoruz.
<?php
namespace App\DataTables;
use App\Models\Customers;
use Yajra\DataTables\DataTableAbstract;
use Yajra\DataTables\Html\Builder;
use Yajra\DataTables\Html\Button;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Services\DataTable;
class CustomersDataTable extends DataTable
{
/**
* Build DataTable class.
*
* @param mixed $query Results from query() method.
* @return \Illuminate\Http\JsonResponse
*/
public function dataTable($query)
{
return datatables()
->eloquent($query)
->addColumn('action', static function (Customers $customer) {
return "<a href='/customers/detail/{$customer->slug}' class='btn btn-success btn-sm w-100'>Detail</a>";
})
->toJson();
}
/**
* Get query source of dataTable.
*
* @param Customers $model
* @return \Illuminate\Database\Eloquent\Builder
*/
public function query(Customers $model)
{
return $model->newQuery();
}
/**
* Optional method if you want to use html builder.
*
* @return Builder
*/
public function html()
{
$columns = $this->getColumns();
return $this->builder()
->setTableId('customers-table')
->columns($columns)
->language( "//cdn.datatables.net/plug-ins/1.10.21/i18n/Turkish.json" )
->minifiedAjax()
->orderBy(0)
->buttons(
Button::make('reset')
)
->ajax([
'url' => '/customers/list-datatable',
"type" => 'POST',
]);
}
/**
* Get columns.
*
* @return array
*/
protected function getColumns()
{
return [
Column::make('id')->title('ID'),
Column::make('name')->title('Name'),
Column::make('email')->title('E-Mail'),
Column::make('phone')->title('Phone Number'),
Column::make('address')->title('Address'),
Column::make('action')->title('#'),
];
}
/**
* Get filename for export.
*
* @return string
*/
protected function filename()
{
return 'Customers_' . date('YmdHis');
}
}
İçeriğini açıklayalım.
public function dataTable($query)
{
return datatables()
->eloquent($query)
->addColumn('action',
static function (Customers $customer) {
return "<a href='/customers/detail/{$customer->slug}' class='btn btn-success btn-sm w-100'>Detail</a>";
})
->toJson();
}
Bu kısım bizim model ile bağlantımızı sağlayıp tablomuzu oluşturan kısım var olan verimize ek olarak bir kolon eklemek istiyorsak addColumn ile ekliyoruz. İlk parametre isimlendirme ikinci parametre ise bizim bu kolona ekleyeceğimiz verinin içeriğini ifade ediyor.
public function html()
{
return $this->builder()
->setTableId('customers-table')
->columns( $this->getColumns() )
->language( "//cdn.datatables.net/plug-ins/1.10.21/i18n/English.json" )
->minifiedAjax()
->orderBy(0)
->buttons(
Button::make('reset')
)
->ajax([
'url' => '/customers/list-datatable',
"type" => 'POST',
"headers" => [
'X-CSRF-TOKEN'=> csrf_token()
]
]);
}
Burası bizim tablomuzun şablonunu oluşturan kısım.
setTableId : Tablomuza ek olarak bir id eklemek için kullanılır.
columns : Tablomuzun kolonlarını belirler.
language : Tablonun dilini ifade eder.( Default : English )
minifiedAjax : Gereksiz sorguları kaldırır.
orderBy : Belirtilen kolonun sıralama şeklini ifade eder.
buttons : Tabloya eklenecek olan butonlar( print, export, reset)
ajax.url : İsteğin yapılacağı url
ajax.type : İsteğin tipi
ajax.header.x-csrf-token : Post isteğimiz için gerekli olan token
Ek olarak Ajax için geçerli olan parametreleri geçebilirsiniz.
Kolonları belirleyen kısmı inceleyelim.
protected function getColumns()
{
return [
Column::make('id')->title('ID'),
Column::make('name')->title('Name'),
Column::make('email')->title('E-Mail'),
Column::make('phone')->title('Phone Number'),
Column::make('address')->title('Address'),
Column::make('action')->title('#'),
];
}/**
* Make : Veritabanında ki kolonumuzun adını ifade ediyor.
* Eğer relationship kullanıyorsanız ek olarak düzenlemeniz gerekiyor.
* Örneğin: Column::make('authorized_person.0.full_name')
* ->name('authorized_person.full_name')
* ->title('Authorized Person')
* Eğer relationship veriniz Array ise "name" belirtmelisiniz.
* Yoksa arama sırasında Veritabanı sorgusu hatası alırsınız.
*
* title: Tablonun Üst kısmında yazmasını istediğimiz isim.
*/
Şimdi “Controller” içerisinde düzenlemelerimizi yapalım. Öncelikli olarak View ekranımızı yapalım.
# CustomersController ...
use App\DataTables\CustomersDataTable;
...public function list(CustomersDataTable $dataTable)
{
return $dataTable->render('app.customer.list');
}
Burada dikkat etmeniz gereken normalde kullandığımız return view(“”) yerine bağımlılık olarak eklediğimiz CustomerDataTable sınıfı üzerinden render fonksiyonunu çağırıyor olmamız. Bu sayede public/app/customer/list.blade.php yi çalıştırması gerektiğini söyledik. Blade içerisine sadece datatable ile ilgili kısımları ekliyorum. Siz temanıza uygun şekilde düzenlersiniz.
@extends('layouts.app')
@section('title', 'Müşteri Listele')
@section('content')
<section class="section">
<div class="section-header">
<h1>@yield('title')</h1>
</div>
<div class="section-body">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="table-responsive">
{!! $dataTable->table() !!}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
@endsection@push('stylesheet')
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.22/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/1.0.3/css/buttons.dataTables.min.css">
@endpush
@push('javascript')
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.22/js/dataTables.bootstrap4.min.js"></script>
<script src="https:///cdn.datatables.net/select/1.3.0/js/select.bootstrap4.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.0.3/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.6/js/dataTables.buttons.min.js"></script>
<script src="/vendor/datatables/buttons.server-side.js"></script>
{!! $dataTable->scripts() !!}
@endpush{!! $dataTable->table() !!} // Tablomuzu oluşturan kısım
{!! $dataTable->scripts() !!} // Gerekli olan Js dosyalar
Tarayıcı üzerinden /customers/list i ziyaret ettiğimizde şöyle bir hata alacağız.
Bu hatanın sebebi ajax da ayarladığmız route a erişemiyor olması şimdi o kısmı düzenleyeceğiz. Aslında işimizi en basitleştirdiği kısıma geldik kütüphanenin tekrar routes/web.php yi şu şekilde düzenliyoruz.
namespace App\Http\Controllers;
use App\DataTables\CustomersDataTable;
use App\Example\Business\Abstraction\ICustomersService;
use Illuminate\Http\Request;
class CustomersController extends Controller
{
public function list(CustomersDataTable $dataTable)
{
return $dataTable->render('app.customer.list');
}
public function listForDatatable(
Request $request,
CustomersDataTable $dataTable,
ICustomersService $service
)
{
if ($request->ajax()) {
return $dataTable->dataTable(
$service->listForDatatable()
);
}
}
}CustomersDataTable : Oluşturduğumuz DataTables sınıfı
ICustomersService : En başta oluşturduğumuz İş katmanımızın içindeki listForDatatable fonksiyonunu çağırıyoruz buradan veri katmanına ve en son veritabanına erişeceğiz.
Şimdi iş katmanında ki düzenlemeleri yapıyoruz.
namespace App\Example\Business\Concrete;
use App\Example\Business\Abstraction\ICustomersService;
use App\Example\DataAccess\Abstraction\ICustomersDataService;
class CustomersManager implements ICustomersService
{
protected $dataService;
public function __construct(ICustomersDataService $dataService)
{
$this->dataService = $dataService;
}
public function listForDatatable()
{
return $this->dataService->listForDatatable();
}
}Kurucu metot üzerinde Veri Katmanını sistemimize ekledik ve bir değişkene taşıdık. Böylelikle İş katmanı içerisinde istediğimiz fonksiyon içerisinden erişebileceğiz.
Sırada Veri katmanı düzenlemek var.
namespace App\Example\DataAccess\Concrete;
use App\DataTables\CustomersDataTable;
use App\Example\DataAccess\Abstraction\ICustomersDataService;
use App\Models\Customers;
class CustomersDataManager implements ICustomersDataService
{
protected $model;
protected $dataTable;
public function __construct(CustomersDataTable $dataTable)
{
$this->model = new Customers();
$this->dataTable = $dataTable;
}
public function listForDatatable()
{
return $this->dataTable->query( $this->model );
}
}$this->model = new Customers(); yeni bir model instance oluşturuyoruz. Ayrıca DataTable sınıfımızı buraya bağımlılık olarak ekliyoruz ve query sınıfı içine instance gönderiyoruz.
Gördüğünüz gibi pagination için sorguya limit eklemek ve sayfa numarası iletmek arama yapılan kelime için where like gibi ifadeler yazmaktan tamamen bizi kurtarıyor sadece model kütüphanemize gönderiyoruz gerisini o hallediyor iyi ki var :) şimdi sayfamızı yenileyip sonuca bakalım.
- Target [App\\Example\\Business\\Abstraction\\ICustomersService] is not instantiable.
hatası aldık. Bunun sebebi İş ve Veri katmanlarında kullandığımız interfacelerin hangi sınıflara bağlı olduğunu belirtmemiş olmamız son olarak bunu belirtiyoruz. Şu şekilde
php artisan make:provider Example
sonrasında provider ı sisteme tanımlıyoruz.
#config/app.php'providers' => [...
App\Providers\Example::class];
ve ardından provider kısmına bağlantılarımızı yapıyoruz. Detay için laravel dökümantasyonundan providers kısmını inceleyebilirsiniz.
# ExampleProvider public function register()
{
$this->app->singleton('App\Example\Business\Abstraction\ICustomersService','App\Example\Business\Concrete\CustomersManager');
$this->app->singleton('App\Example\DataAccess\Abstraction\ICustomersDataService', 'App\Example\DataAccess\Concrete\CustomersDataManager');
}
böylelikle birbirlerine bağlamış ardından otomatik oluşturulan tanımlamaları ve önbelleği siliyoruz.
composer dump-autoload
php artisan optimize:clear
ve Sonuç
Herkese İyi ve Hatasız Kodlamalar :)
Projenin kodlarına github üzerinden erişebilirsiniz.
Tolga Karabulut
tolga.karabulut@medianova.com
Medianova CDN | Senior PHP Developer