Drupal’de core’da bulunan entity’lerde oluşturulan her field için db normalizasyon kuralları gereği veri tabanında iki tablo oluşturulması sebebiyle bazı verilerinizin db’de daha az yer tutmasını istediğiniz durumlarda database tarafındaki custom tablo’da bu verilerin saklanmasını isteyebilirsiniz. Örneğin bir içeriğin kullanıcılar tarafından tepkilerini ölçmek istediğiniz de ID, Nid, Uid, Oy, Tarih gibi alanlara sahip bir Custom Entity oluşturmak yerine bir tablo oluşturmanız db’de daha az yer kaplayacaktır.
Sizlerde ihtiyacınız olan verileri saklama alanında Entity/Custom Entity yerine Özel Tablo yöntemine karar verdiyseniz bu blog yazımızda sıfırdan ileri seviyeye;
başlıklarına değineceğiz.
Drupal’de bir dizine oluşturulan klasör’ün Drupal tarafından modul olarak algılanması için o klasör adıyla başlayan ve info.yml ile biten bir dosyaya sahip olması gerekmektedir. Drupal’de özel modul oluşturmak için modules/custom dizini kullanılmaktadır.
modules/custom dizinine custom_rate adında bir klasör oluşturalım. İçerine’de custom_rate.info.yml dosyası tanımlayalım.
name: Custom Rate
type: module
description: The description.
package: Custom
core: 8.x
core_version_requirement: ^8 || ^9 || ^10
Name: Modulumuzun extensions sekmesinde görünecek ismi.
Type: Bu klasördeki dosyaların tipini belirler. Modul için module, tema için theme (ek olarak base theme belirtmeniz gerekiyor) yazmanız Drupal’e bu klasör’de bulunan kodların modul veya tema olduğunu tanımlar. Drupal’da modulu/tema’nın tipini eklemiş olduğunuz dizin değil buradaki type parametresi belirler yani dilerseniz Sınırsızlıklar Ülkesi Drupal’de modules/custom altına tema’da tanımlayabilirsiniz.
Description: Modulumuzun extensions sekmesinde görünecek açıklaması.
Package: Modulumuzun extensions sekmesinde hangi grup’da yer alacağı. Örneği kendi firmanızın geliştirmiş olduğunu kodları bir araya getirmek isterseniz buraya firma adınızı yazabilirsiniz.
Core ve core_version_requirement: Modulun uyumlu olduğu core versiyonları.
https://www.drupal.org/docs/develop/creating-modules/let-drupal-know-about-your-module-with-an-infoyml-file adresinden info yml’da kullanabileceğiniz diğer kullanışlı parametreleri inceleyebilirsiniz.
Oluşturmuş olduğumuz custom modul kurulurken arka tarafta custom tablonun’da oluşturulmasını ve kaldırılırken de özel tablonunda kaldırılmasını isteyebilirsiniz. Bunun için modulumuzun klasörünün içerisine modulun_adi.install dosyası oluşturmamız gerekmektedir.
custom_rate klasörümüzün içerisine custom_rate.install dosyasını oluşturuyoruz. İçerisin’de modulun_adı_schema hook’unu kullanarak modul kaldırıldığında veya eklendiğinde oluşturulacak tabloyu belirtiyoruz.
function custom_rate_schema() {
$schema['custom_rate'] = [
'description' => 'Custom Rate İşlemleri',
'fields' => [
'id' => [
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique record ID.',
],
'uid' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Oylayan Kişi',
],
'nid' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Oylanan içerik id',
],
'rate' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Oy',
],
'created' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Oylanan içerik id',
],
'updated' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'Oylanan içerik id',
],
],
'primary key' => ['id'],
'indexes' => [
'id' => ['id'],
],
];
return $schema;
}
$schema['custom_rate'] lle custom_rate adında bir özel tablo oluşturuyoruz. Birden çok tablo tanımlamak isterseniz return $schema’dan önce sınırsız tablo tanımlayabilirsiniz.
'description' => 'Custom Rate İşlemleri': ile tablonun özetini belirtiyoruz. Burada bu tablonun amacını belirtmek daha sonradan database tarafından inceleme yapacak başka bir developer için daha sürdürülebilir olmasını sağlar.
'fields': Bölümünde bu tabloda yer almasını istediğiniz alanları belirtmeniz gerekmektedir.
Aşağıdaki tanımları kullanarak özel tablonuza auto_increment/primary alan ekleyebilirsiniz.
'id' => [
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique record ID.',
],
ve fields’dan sonra da aşağıdaki tanımları kullanmanız gerekmektedir.
'primary key' => ['id'],
'indexes' => [
'id' => ['id'],
],
type: Alanın tipini burada belirleyebilirsiniz.
unsigned: Alanın sadece poziitf değer saklayıp saklamayacağı,
not null: Alanın boş bırakılıp bırakılamayacağı,
default: alanın default değeri Boş gönderildiğinde burada tanımladığınız veriyi saklar.
description: Alanın açıklaması
Bu tanımlardan sonra modulu açtığımızda database tarafında tablomuz aşağıdaki gibi oluşturulmuş olacaktır.
Drupal’de geliştirmiş olduğunuz bir modulun içerisindeki fonksiyonun bir url’e sahip olmasını isteyebilirsiniz. Bu sayede kullanıcılardan veriler okuyup arkaplanda işleyebilirsiniz. Bu işlemleri modulun içerisinde modulun_adi.routing.yml dosyası oluşturarak gerçekleştirebiliriz.
custom_rate.routing.yml
custom_rate.rate_add:
path: '/custom-rate/add/{nid}/{rate}'
defaults:
_title: 'oylama'
_controller: '\Drupal\custom_rate\Controller\CustomRateController::add'
requirements:
_permission: 'rate add'
custom_rate.rate_add: ifadesinde custom_rate. olan ifade modulun adı olmak zorundadır. Noktadan sonra ki bölüm ise o yolun makina adıdır.
path: Cevap verilmesi gereken url
defaults: title: Sayfanın başlığı
defaults:_controller: Path bölümünde tanımlı olan url’e gidildiğinde cevap vermesi gereken fonksiyonu burada tanımlıyoruz. Eğer bir controller içerisindeki fonksiyonun değilde bir form’un cevap vermesini istiyorsak _form: olarak tanım yapılmalıdır.
Requirements: _permission: belirtilen izne sahip olan herkes bu url’e erişebilir.
Routing dosyasında kullanabileceğiniz daha fazla parametre için https://www.drupal.org/docs/drupal-apis/routing-system/structure-of-routes adresini inceleyebilirsiniz.
Drupal’de custom tablo içerisinde oluşturmuş olduğunuz tablo’ya içerik eklemek istediğinizde database insert yöntemini kullanmanız gerekmektedir. Şuanki örneğimizde url’den değer alıp database’e ekleyen kodumuzu oluşturalım.
Url’den içeriğin id’sini ve kullanıcının oyunu alan bir routing tanımlayalım:
custom_rate.rate_add:
path: '/custom-rate/add/{nid}/{rate}'
defaults:
_title: 'oylama'
_controller: '\Drupal\custom_rate\Controller\CustomRateController::add'
requirements:
_permission: 'rate add'
routing.yml’da path bölümünde kullanıcıdan dinamik veri almak istiyorsanız {} içerisinde değişken adını tanımlamalısınız. Örneği kullanıcıdan nid ve rate bilgisini almak istiyorsanız path /custom-rate/add/{nid}/{rate} olmalıdır. Bu değişkenleri de _controller paremetresinin tanımlı olduğu fonksiyon içerisinde alıp kullanabilirsiniz.
public function add($nid, $rate) {
try {
$nid = Xss::filter($nid);
$rate = Xss::filter($rate);
$uid = \Drupal::currentUser()->id();
$currentTime = \Drupal::time()->getCurrentTime();
$con = \Drupal\Core\Database\Database::getConnection();
$con->insert('custom_rate')
->fields(['uid' ,'nid','rate', 'created','updated'])
->values([$uid, $nid, $rate, $currentTime, $currentTime])
->execute();
return new JsonResponse(['data' => $con->lastInsertId()]);
}
catch (\Throwable $e) {
return new JsonResponse(['data' => 0, 'hata' => $e->getMessage()]);
}
}
url’den değer aldığımız için sql injection’dan korunmak amaçlı aldığımız değerleri XSS:filter fonksiyonundan geçirmemiz gerekmektedir. XSS::filter’ın çalışması için sayfanın başına use Drupal\Component\Utility\Xss; tanımlamanız gerekmektedir.
$con = \Drupal\Core\Database\Database::getConnection();
$con->insert('custom_rate')
->fields(['uid' ,'nid','rate', 'created','updated'])
->values([$uid, $nid, $rate, $currentTime, $currentTime])
->execute();
Yukarıdaki komutlarda custom_rate tablosuna, fields bölümündeki belirtilen alanlara sırasıyla values bölümündeki belirtilen değerleri eklemektedir. Yani custom_rate tablosundaki uid alanına values bölümünde ilk sırada belirtilen $uid değişkeninin değerini ekmektedir. $con->lastInsertId() ile de ekleme işlemi başarılı ise primary olarak belirlenen alanın id’sini alabiliriz.
Drupal’de custom tablo’ya eklemiş olduğumuz değeri daha sonradan değiştirmek isteyebiliriz bu işlem için database update yöntemini kullanabilirsiniz.
Url’den değer alıp controller’daki fonksiyonuna yönlendirmek için routing.yml’a aşağıdaki tanımı yapıyoruz.
custom_rate.rate_update:
path: '/custom-rate/update/{nid}/{rate}'
defaults:
_title: 'oylama'
_controller: '\Drupal\custom_rate\Controller\CustomRateController::update'
requirements:
_permission: 'rate update'
Controller içerisindeki update fonksiyonumuz aşağıdaki gibi olmalıdır.
public function update($nid, $rate) {
try {
$nid = Xss::filter($nid);
$rate = Xss::filter($rate);
$uid = \Drupal::currentUser()->id();
$currentTime = \Drupal::time()->getCurrentTime();
$con = \Drupal\Core\Database\Database::getConnection();
$con->update('custom_rate')
->fields(['rate' => $rate, 'updated' => $currentTime])
->condition('uid', $uid)
->condition('nid', $nid)
->execute();
return new JsonResponse(['data' => 'guncelendi']);
}
catch (\Throwable $e) {
return new JsonResponse(['data' => 0, 'hata' => $e->getMessage()]);
}
}
Update fonksiyonumuzda insert’den farklı olarak key=> value eklemesi yapmamız gerekmektedir. Yukarıdaki örnekte rate alanına $rate değişkeninin değeri gönderilmektedir İnsert’den farklı olarak condition’lar kullanabilirsiniz. Yani uid’si belirttiğim uid’ye, nid’si belirttiğim nid’ye eşit olan değer’in alanlarını değiştirmek istiyorsanız yukarıdaki kodu kullanabilirsiniz.
Drupal’de bazen istenen değerlere sahip veri var mı bilemeyiz. Bunun için ilk aklımıza gelen yöntem olan database select’i kullanıp veri varsa update yoksa insert kodlarını çalıştıran kod yazabiliriz ancak bu hem yöntem olarak eski hemde kod kalabalığı oluşturmuş olur. Bu işlemi drupal’de gerçekleştiren database merge yöntemini kullanabiliriz.
Url’den değer alıp varsa güncelleyen yoksa oluşturan kodumuzu yazalım. Bunun için routing.yml’a aşağıdaki gibi bir tanım yapmamız gerekmektedir.
custom_rate.rate_merge:
path: '/custom-rate/merge/{nid}/{rate}'
defaults:
_title: 'oylama'
_controller: '\Drupal\custom_rate\Controller\CustomRateController::merge'
requirements:
_permission: 'rate add'
Ardından _controller içerisinde belirtmiş olduğumuz fonksiyonumuzda verileri okuyup işlemleri gerçekleştirebiliriz.
public function merge($nid, $rate) {
try {
$nid = Xss::filter($nid);
$rate = Xss::filter($rate);
$uid = \Drupal::currentUser()->id();
$currentTime = \Drupal::time()->getCurrentTime();
$con = \Drupal\Core\Database\Database::getConnection();
$con->merge('custom_rate')
->keys(['uid' => $uid, 'nid' => $nid])
->insertFields(array(
'uid' => $uid,
'nid' => $nid,
'rate' => $rate,
'created' => $currentTime,
'updated' => $currentTime,
))
->updateFields(array(
'rate' => $rate, // update time
'updated' => $currentTime, // update time
))
->execute();
return new JsonResponse(['data' => 'merge']);
}
catch (\Throwable $e) {
return new JsonResponse(['data' => 0, 'hata' => $e->getMessage()]);
}
}
Merge yönteminde conditionları keys bölümüne yazıyoruz diyebiliriz. Keys bölümünde eşleşen değerlere sahip veri varda UpdateFields bölümündeki güncellemeler çalışır yoksa insertField bölümündeki eklemeler çalışır. Bu şekilde data modern ve az satırlara sahip kod’la işlemlerimizi tamamlamış oluruz.
Drupal’de custom tabloya eklediğimiz değerleri silmek istediğimizde delete yöntemini kullanabiliriz.
Url’den değer alıp silme işlemini yapan kodumuzu yazalım. Bunun için routing.yml’a aşağıdaki gibi bir tanım yapmamız gerekmektedir.
custom_rate.rate_delete:
path: '/custom-rate/delete/{nid}'
defaults:
_title: 'oylama'
_controller: '\Drupal\custom_rate\Controller\CustomRateController::delete'
methods: [GET]
requirements:
_permission: 'rate add'
Ardından _controller içerisinde belirtmiş olduğumuz fonksiyonumuzda verileri okuyup işlemleri gerçekleştirebiliriz.
public function delete($nid) {
try {
$nid = Xss::filter($nid);
$uid = \Drupal::currentUser()->id();
$con = \Drupal\Core\Database\Database::getConnection();
$con->delete('custom_rate')
->condition('uid', $uid)
->condition('nid', $nid)
->execute();
return new JsonResponse(['data' => 'silindi']);
}
catch (\Throwable $e) {
return new JsonResponse(['data' => 0, 'hata' => $e->getMessage()]);
}
}
Delete fonksiyonunda condition bölümünde belirtilen şartlara uygun olan veri varsa silme işlemi gerçekleştirilir.
Drupal’da custom tabloda oluşturmuş olduğunuz verilere ulaşıp işlemek isterseniz database select yöntemini kullanabilirsiniz. Select yönteminde condition ile şartları belirleyip şartlara uyanlar arasından field bölümünde belirtmiş olduğunuz alanların verisini alabilirsiniz
$query = \Drupal::database()->select('custom_rate', 't');
$query->fields('t', ['uid', ‘nid’]);
$query->condition('nid', $nid)
$query->distinct();
$result = $query->execute()->fetchAll();
Bu yöntemi kullanarak veritabanından değerler çekip kullanıcıya istersek json, istersek tablo, istersek xlsx gibi formatlarda çıktılar sunabilirsiniz. Ancak bu yöntemi kullanarak devam ederseniz herbiri için kod yazmanız gerekmektedir. Yani sadece adminlerin görebileceği bir sayfada tablo olarak görmek isteyebilirsiniz, bu tabloyu xlsx olarak indirebilmek isteyebilirsiniz. Bunlar için tek tek kod yazmanız gerekmektedir. Peki kod yazmadan daha profesyonel nasıl dataları işleriz diyorsanız bir sonraki başlığımızdan devam edebilirsiniz 🙂
Drupal’de Custom tablo’da saklamış olduğunuz verileri kullanıcılara tablo şeklinde sunmak isteyebilirsiniz. Kullanıcıların talebi üzerine bu verileri belirli rollere xlsx olarak download etmesini sağlamanız gerebilir. Dış sistemlere veriyi JSON olarak sunmak isteyebilirsiniz. Block oluşturup herhangi bir region’a bu değerleri basmak isteyebilirsiniz. Bunların hepsine pager eklemek isteyebilirsiniz. Bu işlemlerin hepsini yapmak istediğinizde ilk aklımıza gelen kodlarla yapabilirim seçeneğini seçtiğinizde ciddi zaman kayıpları yaşayabilirsiniz. Bu durumun önüne geçebilmek için Drupal Entity yapısında kullanılan view’ı kullanabilsek süper olurdu ifadesini duyar gibiyiz :) Evet bu custom tabloda da mümkün. Custom olarak oluşturmuş olduğunuz tablolaki verileri Drupal’in güçlü View yapısını ve bileşenlerini kullanarak kullanıcılara sunabilmeniz mümkündür. Özel tabloyu view ile bağlamak için hook_views_data hook’unu kullanmaktayız. Bu hook’u modulunuzun içerisinde oluşturulmuş veya oluşturacağız module_adi.module dosyasında tanımlayarak custom tablo ile view’ı bağlayabilirsiniz. Dilerseniz bu işlemi https://www.drupal.org/project/view_custom_table modulunu kullanarak gerçekleştirebilirsiniz veya bu modulun kullanmış olduğu aşağıda detaylı değineceğimiz fonksiyonları kullanarak sizler de gerçekleştirebilirsiniz.
Şimdi örneğimize başlayalım. custom_rate.module dosyasında custom_rate_views_data fonksyionunu tanımlayalım. Tablomuzun adı custom_rate olduğu için data[custom_rate] olmalıdır.
function custom_rate_views_data() {
$data['custom_rate']['table']['group'] = t('Custom Rate');
$data['custom_rate']['table']['base'] = array(
'title' => t('Custom Rate'),
'help' => t('Integration with Custom Rate Table'),
);
}
Title bölümünde belirtmiş olduğumuz ifade view ekleme sayfasında (/admin/structure/views/add) View settings bölümünde kullanacağınız ismi belirtir. Custom Rate’i seçerek devam ederseniz bu hook’dan tanımladığımız verileri kullanarak view oluşturabileceksiniz.
Custom Tablolarda relationship yapmak istiyorsak; yani bizim örneğimizde nid: node entity’sinin id’si, uid: user entity’sinin id’sidir. Bizler bu id’yi vererek o node’daki veya user’daki herhangi bir alana erişmek istiyorsak relationship yapmamız gerekmektedir. Views’da relationship yapabilmek için table join’e node’ların tutulduğu node_field_data ve userların bulunduğu users_field_data tablolarını tanımlıyoruz. left_field bölümüne bizim tablomuzdaki alanı, field bölümünde drupal tarafındaki tablodaki alanı belirtiyoruz.
$data['custom_rate']['table']['join'] = array(
'node_field_data' => array(
'left_field' => 'nid',
'field' => 'node_id',
),
'users_field_data' => array(
'left_field' => 'uid',
'field' => 'user_id',
),
);
Ardından views’da relationship alanında kullanabilmek için aşağıdaki gibi relationship tanımı yapıyoruz.
$data['custom_rate']['nid'] = [
'title' => 'Rate Node Id',
'field' => [
'id' => 'numeric',
],
'sort' => [
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
],
'argument' => [
'id' => 'numeric',
],
'relationship' => [
'base' => 'node_field_data',
'base field' => 'nid',
'id' => 'standard',
'label' => 'Rate Node Relationship',
],
];
Buradaki alanlar:
İlgili alanlarda tanım varsa views’ın ilgili bölümünde kullanılabilir olur. Tanım yoksa ilgili bölümde Add’e tıklayınca ilgili alanı göremezsiniz. Örneğin Sort tanımı yapılmasaydı nid değeri views’ı sıralama bölümünde gelmezdi.
Title: custom tablo’daki alanın view’da kullanılacak başlığı.
Field: Views’ın field bölümünde bu alanın verilerinın hangi türden render edileceği bu alanda belirtiliyor. Yukarıdaki Örnekte numeric olarak render edileceği belirtilmiş.
Sort: Views’ın sıralama bölümünde ilgili alanın tipinin ne olacağı belirtilmelidir.
Filter: Views’ın filtreleme bölümünde ilgili alanın tipinin ne olacağı belirtilmelidir.
Argument: Views’ın Contextual filter bölümünde ilgili alanın tipinin ne olacağı belirtilmelidir.
Relationship: Views’ın relationship bölümünde Drupal’daki hangi entity ile bağlanacağı belirtilmelidir.
Views’daki alanlar aşağıda belirlenmiştir.
Son durumda örnek kodumuz aşağıdaki gibidir;
<?php
/**
* @file
* Primary module hooks for Custom Rate module.
*
* @DCG
* This file is no longer required in Drupal 8.
* @see https://www.drupal.org/node/2217931
*/
function custom_rate_views_data() {
$data['custom_rate']['table']['group'] = t('Custom Rate');
$data['custom_rate']['table']['base'] = array(
'title' => t('Custom Rate'),
'help' => t('Integration with Custom Rate Table'),
);
$data['custom_rate']['table']['join'] = array(
'node_field_data' => array(
'left_field' => 'nid',
'field' => 'node_id',
),
'users_field_data' => array(
'left_field' => 'uid',
'field' => 'user_id',
),
);
$data['custom_rate']['id'] = [
'title' => 'Rate Id',
'field' => [
'id' => 'numeric',
],
'sort' => [
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
],
'argument' => [
'id' => 'numeric',
],
];
$data['custom_rate']['nid'] = [
'title' => 'Rate Node Id',
'field' => [
'id' => 'numeric',
],
'sort' => [
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
],
'argument' => [
'id' => 'numeric',
],
'relationship' => [
'base' => 'node_field_data',
'base field' => 'nid',
'id' => 'standard',
'label' => 'Rate Node Relationship',
],
];
$data['custom_rate']['uid'] = [
'title' => 'Rate User Id',
'field' => [
'id' => 'numeric',
],
'sort' => [
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
],
'argument' => [
'id' => 'numeric',
],
'relationship' => [
'base' => 'users_field_data',
'base field' => 'uid',
'id' => 'standard',
'label' => 'Rate User Relationship',
],
];
$data['custom_rate']['rate'] = [
'title' => 'Rate',
'field' => [
'id' => 'numeric',
],
'sort' => [
'id' => 'standard',
],
'filter' => [
'id' => 'numeric',
],
'argument' => [
'id' => 'numeric',
],
];
$data['custom_rate']['created'] = [
'title' => 'Rate Created',
'field' => [
'id' => 'date',
],
'sort' => [
'id' => 'date',
],
'filter' => [
'id' => 'date',
],
'argument' => [
'id' => 'date',
],
];
$data['custom_rate']['updated'] = [
'title' => 'Rate Updated',
'field' => [
'id' => 'date',
],
'sort' => [
'id' => 'date',
],
'filter' => [
'id' => 'date',
],
'argument' => [
'id' => 'date',
],
];
return $data;
}
Daha fazla tip tanımı için https://api.drupal.org/api/drupal/core%21modules%21views%21views.api.php/function/hook_views_data/8.9.x adresini ziyaret edebilirsiniz.
Bu tanımdan sonra ilgili tablodaki alanları dilediğiniz çıktıya views ve modulleri yardımıyla dönüştürebilirsiniz.
GOSB Teknopark Hi-Tech Bina 3.Kat B3 Gebze - KOCAELİ