Merhaba sevgili kod meraklıları! Bugün, PHP ile oynayacağımız güzel bir oyun var: Kendi ORM (Object-Relational Mapping) sınıfımızı nasıl yaparız? Evet, evet, PDO kullanarak kendi ORM’imizi inşa edeceğiz. Hazır mısınız? Hadi başlayalım!
Adım 1: Temel Bir Model Sınıfı Oluşturalım
İlk adım olarak, bir Model sınıfı oluşturuyoruz. Bu sınıf, veritabanı işlemlerimizi yönetecek. Temelde bir tablo ile yapacağımız işlemleri satır bazında nesneleştirecek. Sınıfımız PDO ile çalığacağı için oluşturucumuza öncelikle bağlantı kurduğumuz PDO kaynağını vermeliyiz.
class Model
{
protected $primaryKey = 'id'; // Varsayılan anahtar sütunu
protected $attributes = [];
protected $isNew = true;
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->table = $this->getTableName();
}
// ...
protected function getTableName()
{
// Eğer tablo adı belirtilmemişse, sınıf adından tablo adını çıkart
$className = get_called_class();
$parts = explode('\\', $className);
$className = end($parts);
return strtolower($className) . 's'; // Örnek: Musteriler tablosu için "musterilers"
}
}
Adım 2: Save ve Reload Metodlarını Ekleyelim
Haydi, modelimize biraz hareket katmak için save ve reload metodlarını ekleyelim. Modelimizin bir örneğini oluşturduğumuzda $model->save() komutu ile model üzerine işlediğimiz verileri veri tabanın da varsa güncelleyecek yoksa ekleyeceğiz. İşte bizi heyecanlandıran kısımlar!
public function save()
{
if ($this->isNew) {
$this->insert();
} else {
$this->update();
}
}
protected function insert()
{
$columns = implode(', ', array_keys($this->attributes));
$values = ':' . implode(', :', array_keys($this->attributes));
$stmt = $this->pdo->prepare("INSERT INTO $this->table ($columns) VALUES ($values)");
foreach ($this->attributes as $key => $value) {
$stmt->bindValue(":$key", $value);
}
$stmt->execute();
$this->isNew = false;
$this->{$this->primaryKey} = $this->pdo->lastInsertId();
}
protected function update()
{
$setClause = '';
foreach ($this->attributes as $key => $value) {
$setClause .= "$key=:$key, ";
}
$setClause = rtrim($setClause, ', ');
$stmt = $this->pdo->prepare("UPDATE $this->table SET $setClause WHERE $this->primaryKey = :id");
$stmt->bindParam(':id', $this->{$this->primaryKey}, PDO::PARAM_INT);
foreach ($this->attributes as $key => $value) {
$stmt->bindValue(":$key", $value);
}
$stmt->execute();
}
public function reload()
{
if (!$this->isNew) {
$stmt = $this->pdo->prepare("SELECT * FROM $this->table WHERE $this->primaryKey = :id");
$stmt->bindParam(':id', $this->{$this->primaryKey}, PDO::PARAM_INT);
$stmt->execute();
$this->attributes = $stmt->fetch(PDO::FETCH_ASSOC);
}
}
Adım 3: Delete ve Duplicate Metodlarıyla Bir Adım Öteye
Şimdi, biraz eğlenmek için “delete” ve “duplicate” metodlarını ekleyelim. Tablomuzdan bir satır silmemiz veya çoğaltmamız gerekebilir bu nedenle “delete” ve “duplicate” adında iki metod daha ekliyoruz.
public function delete()
{
if (!$this->isNew) {
// Kaydı silme işlemleri burada!
$stmt = $this->pdo->prepare("DELETE FROM $this->table WHERE $this->primaryKey = :id");
$stmt->bindParam(':id', $this->{$this->primaryKey}, PDO::PARAM_INT);
$stmt->execute();
$this->isNew = true;
echo "Kayıt başarıyla silindi! Şaka bir yana, gerçekten sildik mi bakalım?";
}
}
public function duplicate()
{
$clone = clone $this;
$clone->isNew = true;
$clone->save();
echo "Klonlama tamamlandı! Şimdi iki tane var, biri gerçek biri sahte!";
return $clone;
}
Adım 4: Dynamic Find Metodlarıyla Araştırma Zamanı!
Buraya kadar geldik ve sıkılmadık! Şimdi, dinamik find metodları ekleyerek modelimizi daha güçlü kılacağız. Find metodlarımız bize Modelimizin işaret ettiği db tablosunda aramalar yapmamızda yardımcı olacak ve kolayca sonuçları alacağız. Find koşulumuz ile eşleşen sonuçları bir dizi olarak bize çevirirken findFirst eşleşen ilk değeri tek bir öğe olarak döndürür.
public static function find($conditions = [], $limit = null)
{
$model = new static(self::$pdo);
$whereClause = $model->buildWhereClause($conditions['conditions'] ?? []);
$limitClause = ($limit !== null) ? "LIMIT $limit" : '';
$orderByClause = $conditions['orderby'] ?? '';
$stmt = $model->pdo->prepare("SELECT * FROM $model->table $whereClause $orderByClause $limitClause");
$model->bindValues($stmt, $conditions['bind'] ?? []);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
$collection = [];
foreach ($results as $result) {
$model = new static($model->pdo);
$model->attributes = $result;
$model->isNew = false;
$collection[] = $model;
}
return $collection;
}
public static function findFirst($conditions = [])
{
$model = new static(self::$pdo);
$whereClause = $model->buildWhereClause($conditions['conditions'] ?? []);
$stmt = $model->pdo->prepare("SELECT * FROM $model->table $whereClause LIMIT 1");
$model->bindValues($stmt, $conditions['bind'] ?? []);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
$model = new static($model->pdo);
$model->attributes = $result;
$model->isNew = false;
return $model;
}
return null;
}
public function count($conditions = [])
{
$whereClause = $this->buildWhereClause($conditions['conditions'] ?? '');
$stmt = $this->pdo->prepare("SELECT COUNT(*) as count FROM $this->table $whereClause");
$this->bindValues($stmt, $conditions['bind'] ?? []);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return ($result) ? (int)$result['count'] : 0;
}
protected function buildWhereClause($conditions)
{
if (empty($conditions)) {
return '';
}
$whereClause = 'WHERE ';
foreach ($conditions as $condition) {
$whereClause .= "$condition AND ";
}
$whereClause = rtrim($whereClause, ' AND ');
return $whereClause;
}
protected function bindValues($stmt, $bind)
{
foreach ($bind as $key => $value) {
if (is_array($value)) {
// Eğer değer dizi ise, IN (?) kullanarak bağla
$inClause = implode(', ', array_fill(0, count($value), '?'));
$stmt->bindValue(":$key", $inClause);
foreach ($value as $index => $item) {
$stmt->bindValue($index + 1, $item);
}
} else {
$stmt->bindValue(":$key", $value);
}
}
}
Adım 5: Özel Arama Metodları ve Magic Methods
Şimdi en sevdiğimiz bölüme geldik: Özel arama metodları! Aradığımız satıra hızlıca erişmek için kolon değerlerinden faydalanan metodlar yapacağız öncelikli olarak findById($id) verdiğimiz id değeri ile eşleşen sonucu almamızı sağlayacak. Biraz da sihir ekleyerek tüm kolonlar için benzer bir arama yapabileceğiz. findBy{Kolonadı}($kolonDegeri) şeklinde doğrudan erişim sağlayacağız.
public function __get($name)
{
if (array_key_exists($name, $this->attributes)) {
return $this->attributes[$name];
}
return null;
}
public function __set($name, $value)
{
$this->attributes[$name] = $value;
}
public static function findById($id)
{
return static::findFirst([
'conditions' => ["$model->primaryKey = :id"],
'bind' => ['id' => $id],
]);
}
public static function __callStatic($method, $arguments)
{
if (strpos($method, 'findBy') === 0) {
$column = lcfirst(substr($method, 6));
return static::findFirst([
'conditions' => ["$column = :$column"],
'bind' => [$column => $arguments[0]],
]);
}
throw new \BadMethodCallException("Method $method does not exist.");
}
Ve işte buradayız, kendi ORM sınıfımızı nasıl oluşturacağımızı adım adım anlattık! Umarım hem öğretici hem de eğlenceli bir süreç geçirmişsinizdir. Şimdi kod dünyasına biraz daha hakim oldunuz! Şimdi, bu model sınıfınızla gerçek projelerde dans etmeye hazır mısınız?
Haydi Model Sınıfımızı Kullanarak Bir Örnek Çocuk Sınıf Üretelim
Veri tabanımızda erişmek istediğimiz tablomuzun adı “müsteriler” olsun. Müşteriler tablomuza erişmek için yazacağımız kalıtım aşağıdaki şekilde olacak. Dosya adını Musteriler.php olarak yazabiliriz.
class Musteriler extends Model{
// Önceki özel metodlar burada, eğer musteriler sınıfında kullanacağımız özel metodlarımız olacaksa burada tanımlayabiliriz...
/**
* Örneğin müşteri sayımızı çeviren bir metod yazalım.
**/
public static function countCustomers() {
return static::count();
}
}
Şimdi yazdığımız sınıfı kullanalım ve basit bir şekilde sık karşılaşılan bazı işlemleri yapalım. Müşterileri listeleyelim.
// Sayfa parametresi
$page = 1;
// Her sayfada gösterilecek müşteri sayısı
$perPage = 10;
// Başlangıç indeksi
$offset = ($page - 1) * $perPage;
// Müşterileri sayfa sayfa getir
$musteriListesi = Musteriler::find(['orderby' => 'ORDER BY musteriadi ASC'], $perPage, $offset);
// Toplam müşteri sayısını bul
$toplamMusteriSayisi = Musteriler::countCustomers();
// Blog yazısında kullanılmak üzere sonuçları gösterelim
echo "Toplam Müşteri Sayısı: $toplamMusteriSayisi" . PHP_EOL;
foreach ($musteriListesi as $musteri) {
echo "Müşteri Adı: {$musteri->musteriadi}, Email: {$musteri->email}" . PHP_EOL;
}
Buda yetmezmiş gibi tablomuza müşteri ekleyelim, düzenleyelim, çoğaltalım ve silelim.
// PDO bağlantısını oluşturalım
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
// Model sınıfını oluşturalım ve PDO bağlantısını set edelim
Musteriler::setPDO($pdo);
// Yeni bir müşteri oluşturalım ve kaydedelim
$yeniMusteri = new Musteriler();
$yeniMusteri->musteriadi = 'Yeni Müşteri';
$yeniMusteri->email = 'yeni@ornek.com';
$yeniMusteri->save();
// Oluşturduğumuz müşteriyi güncelleyelim
$yeniMusteri->email = 'guncel@ornek.com';
$yeniMusteri->save();
// Müşteriyi ID'si ile arayalım
$arananMusteri = Musteriler::findById($yeniMusteri->id);
echo "Aranan Müşteri: {$arananMusteri->musteriadi}, Email: {$arananMusteri->email}" . PHP_EOL;
// Müşteriyi klonlayalım ve yeni bir müşteri ekleyelim
$klonMusteri = $arananMusteri->duplicate();
$klonMusteri->musteriadi = 'Klonlanan Müşteri';
$klonMusteri->save();
Bu örnek, oluşturduğumuz Musteriler model sınıfının temel özelliklerini kullanarak bir müşteri eklemeyi, güncellemeyi, aramayı ve klonlamayı göstermektedir. Tabii ki, gerçek bir uygulamada bu işlemler daha karmaşık olabilir, ancak temel mantık aynı kalacaktır.
“ORM Nedir? PHP’de Kendi ORM’imi Nasıl Yaparım?” için 2 yanıt
Çok başarılı olmuş. Kesinlikle faydalanacağım bir yazı. Elinize sağlık hocam.
Çok teşekkür ederim.
Geliştirdiğimiz örnek ana fikri açıklamak için bir yol gösterici niteliğinde. Gerçek bir projede kullanılabilir durumda olsa da bazı geliştirme ve düzeltmeler gerekecektir.
Aklınıza takılan kısımları dile getirmekten çekinmeyin lütfen.