Kategoriler
Teknik

TrimStrings Başınıza Nasıl Bela Olabilir

Laravel, PHP dünyasında popüler bir framework ve bir çok geliştiriciye kolaylık sağlamak için harika araçlar sunuyor. Ancak bazen, bu kolaylıklar beklenmedik sorunlara da yol açabiliyor. Bu yazıda, Laravel’deki TrimStrings middleware’inin nasıl bir problem yaratabileceğinden ve bunu nasıl çözebileceğimizden bahsedeceğim.

TrimStrings Middleware Nedir ve Ne İşe Yarar?

TrimStrings middleware’i, Laravel uygulamalarında form verileri gibi kullanıcıdan gelen isteklerdeki boşlukları otomatik olarak temizlemek için kullanılır. Bu, özellikle form inputlarında kullanıcıların yanlışlıkla başında veya sonunda boşluk bıraktığı durumlarda faydalıdır. Örneğin, bir kullanıcı formda e-posta adresini girerken " [email protected] " gibi başında veya sonunda boşluk bırakabilir. TrimStrings middleware’i bu tür boşlukları kırpar ve sadece "[email protected]" değerini alır.

Bu özellik, gereksiz boşluklardan kaynaklanan hataları önlemek ve daha temiz veri işlemek için oldukça yararlıdır. Ancak, her zaman olduğu gibi, bazı özel durumlarda bu varsayılan davranış istenmeyen sonuçlara yol açabilir.

Olay Nasıl Gelişti?

Brezilya merkezli bir ödeme sağlayıcı ile entegre çalıştığımız bir projede, ödeme sonuçlarını almak ve doğrulamak için bir callback yapısı kullanıyoruz. Ödeme sağlayıcısı, işlem sonucunu bir POST isteği ile sunucumuza gönderiyor ve biz de bu isteğin doğruluğunu kontrol etmek için bir signature/hash doğrulaması yapıyoruz.

Bu doğrulama işlemi oldukça basit bir mantığa dayanıyor:

  1. Sağlayıcıdan gelen veriler alınıyor.
  2. Tüm veriler bir string olarak birleştiriliyor.
  3. Bu string, sağlayıcının bize verdiği secret key ile SHA256 algoritması kullanılarak hash’leniyor.
  4. Bu hash, sağlayıcıdan gelen hash ile karşılaştırılıyor. Eğer aynı ise işlem başarılı sayılıyor, değilse başarısız.

Problemi Nasıl Fark Edildi?

Başlangıçta, gelen bazı valid isteklerin neden reddedildiğini anlamak zor oldu. Ancak, Nginx loglarını incelediğimizde, gelen istekteki full_name parametresinde sondaki boşlukların korunarak gönderildiğini fark ettik. Buna rağmen, bizim sunucumuzda bu boşluklar kırpılmıştı ve bu durum hash doğrulamasında hataya yol açıyordu. İşte o an, TrimStrings middleware’inin bu soruna neden olduğu anlaşıldı.

Çözüm Ne?

Bu tür durumlarla karşılaşmamak için, belirli rotalar veya istekler için TrimStrings middleware’ini devre dışı bırakmak gerekiyor. Laravel 8 ile birlikte sunulan TrimStrings::skipWhen metodu, bu duruma özel bir çözüm sunuyor.

Aşağıda bir provider aracılığıyla bu çözümün nasıl uygulanabileceğini görebilirsiniz:

use Illuminate\Foundation\Http\Middleware\TrimStrings;
use Illuminate\Http\Request;

// ...

TrimStrings::skipWhen(function (Request $request) {
    return $request->is('api/v1/integrations/foo-provider/callback');
});

Bu kod parçası, belirli bir rota için TrimStrings middleware’ini devre dışı bırakıyor. Yani api/v1/integrations/foo-provider/callback rotasından gelen istekler için boşluk kırpılması işlemi yapılmıyor ve böylece hash doğrulama işlemi sorunsuz bir şekilde gerçekleştiriliyor.

Sonuç

Laravel’in sunduğu varsayılan özellikler genellikle işleri kolaylaştırırken, belirli senaryolarda beklenmedik sonuçlar doğurabiliyor. Bu yüzden, proje geliştirme sürecinde kullanılan araçların işleyişine dikkat etmek ve potansiyel etkilerini iyi değerlendirmek çok önemli. TrimStrings middleware’i, çoğu durumda yararlı bir araç olmasına rağmen, bu gibi senaryolarda başınıza bela olabilir. Neyse ki, TrimStrings::skipWhen gibi esnek çözümlerle bu tür sorunların önüne geçmek mümkün.

Kategoriler
Teknik

Yoda Notasyonu: Nedir, Ne Değildir?

Yoda notasyonu nedir?

Yoda koşul programlama stili, daha yaygın adıyla Yoda notasyonu, mantıksal karşılaştırmalarda değişkenin ve sabitin yerini değiştirerek kullanmanızı sağlayan bir programlama tekniğidir. Genellikle koşul ifadeleri, koşullu ifadeler veya ‘if’ blokları olarak adlandırılan bu yapılar, programın belirli koşullar altında hangi eylemleri gerçekleştireceğini belirler.

"Talk is cheap, show me code" yani "Ağzı olan konuşuyor, bana koddan haber ver" ifadesini duymuşsunuzdur. İşte bu durumda, Yoda notasyonunu bir örnek üzerinden göstermek en iyisi olacak. Tipik bir koşul ifadesi şu şekilde görünebilir:

if ($değişken == null) {
    // kod
}

yerine Yoda notasyonu kullanalım:

if (null == $değişken) {
    // kod
}

Yoda notasyonunu neden kullanayım?

Bu konu hakkında bazı görüşler var. İlki mantıksal karşılaştırma yaparken bir değişken tanımlama hatasından kaçınmak. Yani iki adet eşittir işareti == koymak yerine bir adet = işareti koyduğunuzda bir değişken tanımlamış oluyorsunuz ve bu bir hataya sebebiyet veriyor. Erken dönem ve öğrenme aşamaları haricinde bu hatayı yapmış olan birileri varsa ve bunu ne sıklıkla yapıyor öğrenmek isterdim. Zira bu bana zayıf bir argüman gibi geliyor.

Yazı genelinde PHP ve JavaScript programlama dilleri üzerinden gideceğim. Ben if bloğu içerisinde bazen özellikle bir değişkene eşitlik sağlıyorum. Ama bunun hatasını hiç yapmadım. Şöyle ki:

if ($değişken = null) {
    // bu blok çok yalnız
}

Yukarıdaki kod hatalı ve değişken null değerine sahip olduğu ve null değeri de PHP’de falsy olduğu için blok içindeki kodlar çalışmayacak. Bunun yerine

if (null = $değişken) {
    // bunun yalnızlığı geçici
}

Yoda notasyonu ile bu şekilde yazmış olsaydı bir derleme hatasından dolayı yaptığımız hatanın farkına varacak ve düzeltmesini yapacaktık.

Ben bazen şu şekilde kod yazma ihtiyacı duyabiliyorum:

if ($transaction = Transaction::find(5)) {
    $transaction->notify();

    return $transaction;
}

return false;

Bazı programlama dillerinde kabul edilebilir bir durum. Ama bazı programlama dillerinde (Swift, Kotlin ve Python 3.8 sonrasında) buna izin verilmiyor. Örneğin Python’da mors operatörü := ile bu işlemi yapıyorsunuz.

Yoda Notasyonunun Artıları ve Eksileri

Yoda notasyonunun, kod okunabilirliği üzerinde pozitif ve negatif etkileri olduğunu belirtmek önemlidir. Bu notasyonun bazı artıları:

  • Yanlışlıkla atama (=) yerine karşılaştırma (==) yapma hatasını önler.
  • Hatalı kod daha hızlı fark edilir, çünkü çoğu dilde hatalı bir atama denemesi derleme hatası oluşturur.

Bazı olası eksileri:

  • Yoda notasyonu, kodun doğal dil akışına aykırıdır ve bu nedenle bazıları için daha az okunabilir olabilir. Mesela, $değişken == null ifadesi "eğer değişken null ise" olarak okunabilirken, null == $değişken ifadesi "eğer null değişken ise" olarak okunur. İkincisi, doğal dil bakış açısından biraz daha zorlayıcı olabilir.

Yoda Notasyonunun Kullanımı

Yoda notasyonu, genellikle PHP ve C dillerinde gözlemlenir. Bununla birlikte, JavaScript gibi dillerde de kullanılabilir. Bu notasyonun kullanımı genellikle kişisel veya ekip tercihlerine bağlıdır.

Örneğin, WordPress, Yoda koşullarının kullanılmasını bir kodlama standardı olarak belirlemiştir. Bununla birlikte, Symfony gibi diğer projeler bu pratiği tavsiye etmemektedir.

Yoda Notasyonu ve Okunabilirlik

Yoda notasyonunun okunabilirliği, kişinin tercihlerine bağlı olarak değişir. Bazılarına göre bu notasyon kodun anlaşılmasını zorlaştırırken, bazıları ise bu notasyonun kullanılmasının olası hataları önleyerek kodun genel kalitesini artırdığını düşünmektedir. En iyi uygulama genellikle ekibinizin ve projenizin ihtiyaçlarına ve tercihlerine bağlıdır.

Örnek:

Yoda notasyonu olmayan hali:

if (username === "Yoda") {
  console.log("May the Force be with you");
}

Yoda notasyonu olan hali:

if ("Yoda" === username) {
  console.log("May the Force be with you");
}

Sonuç olarak, Yoda notasyonu, programlama tarzı ve kodlama standartları üzerine ilginç bir konudur. Kullanımı kişisel tercihler ve belirli projelerin gerekliliklerine bağlıdır. Ancak, her iki durumda da, kodun okunabilirliği ve anlaşılabilirliği en önemli faktör olmalıdır.

Kaynakça:

Kategoriler
Teknik

MySQL’de “Truncated incorrect DOUBLE value” hatası

Her ne kadar hata içerisinde "DOUBLE value" ibaresi geçse de bu hatayı aldığınızda tablonuzda DOUBLE tipinde bir sütun mevcut olmayabilir. Bu yüzden bir kafa karışıklığı yaratabiliyor. Benim karşılaştığım, VARCHAR türünde bir sütunda INTEGER koşul sorgulama yaptığım sırada hata vermesiydi. Örnek sorguyu paylaşayım:

 update `foo_table` set `foo_type` = 'PC', `foo_table`.`updated_at` = '2022-07-23 12:32:41' where `id` >= 4843051 and `user
_id` = 8081 and `foo_id` = 12 and `foo_type` = 'CT';

Bu sorgudaki problem, foo_id sütunundaki koşulu belirtirken integer olarak koşullandırmamdan kaynaklı. Halbuki bu sütun string türünde veri de tutabilecek türde yani VARCHAR. Bu sebepten dolayı "Truncated incorrect DOUBLE value" hatası aldım. Bunu düzeltmek için foo_id koşulundaki veriyi tırnak içine aldım.

 update `foo_table` set `foo_type` = 'PC', `foo_table`.`updated_at` = '2022-07-23 12:32:41' where `id` >= 4843051 and `user
_id` = 8081 and `foo_id` = '12' and `foo_type` = 'CT';

Ve artık her şey yolunda.

Araştırdığım kadarıyla daha çok UPDATE sorgularında yaşanan bir problem ve MySQL açısından haklı bir serzeniş.

Bu hata ile karşılaşmanıza olası başka bir senaryoda şu şekildeymiş:

update students SET name='Ali' and score=9 where id=1
ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'Ali'

Bu sorgudaki sıkıntı açık, UPDATE yaparken name ve score sütunlarını belirtirken AND ifadesi kullanmak. Normal şartlarda UPDATE sorgusunda güncelleme yapılacak sütunlar tanımlanırken virgül ile ayırılır. Yani:

update students SET name='Ali', score=9 where id=1

Diğer bir çözüm olarak da CAST() fonksiyonu kullanılabilir.

Ek olarak PDO veya Laravel üzerinde Eloquent ORM ile veriyi bind ederken string olarak göndermelisiniz.

Student::where('foo_id', '15')->update([...]);
Student::where('foo_id', '19RDA32')->update([...]);

PDO ile:

$statement = $pdo->prepare('update ...');
$statement->bindParam('foo_id', '15');
// veya
$statement->bindParam('foo_id', '15', \PDO::PARAM_STR);

PDO‘da, bindParam metodunda üçüncü parametre varsayılan olarak \PDO::PARAM_STR olarak tanımlıdır. Bu detayda hatanıza sebep olabilecek durumlardan birisi olabilir.

İyi çalışmalar.

Kategoriler
Teknik

PHP’de Özyinelemeli Closure Kullanımı

Bazen bir çözümü gerçekleştirmek için özyinelemeli bir fonksiyon veya metot kullanma ihtiyacımız olabiliyor. Bu ihtiyaç, benim bir kategoriye ait öğeleri ve o kategorinin alt kategorisine ait öğeleri de içine alacak şekilde Eloquent ORM’de listelemem gerektiğinde ortaya çıktı.

Bunun için, kategori modeline alt kategorilerinin ID’sini getiren bir metot ekledim. İşte bu metodun örneği:

<?php

declare(strict_types=1);

namespace App\Models;

// things...

class Category
{
    public function categories()
    {
        return $this->hasMany(Category::class);
    }

    public function getChildCategoryIDs(): array
    {
        $ids = [];

        $extract = function (Category $category, array &$ids = []) use (&$extract): void {
            $ids[] = $category->id;

            if (isset($category->categories) && is_iterable($category->categories)) {
                foreach ($category->categories as $category) {
                    $extract($category, $ids);
                }
            }
        }

        $extract($this);

        return $ids;
    }
}

Closure Kullanımı: Neden?

Bu örnekte, özyinelemeyi gerçekleştirmek için sınıf içerisinde bir metot oluşturabilirdim. Ancak bu yaklaşımın, metot sayısını artıracağı ve kodun karmaşıklığını potansiyel olarak artırabileceği göz önünde bulundurulduğunda, tüm işlemleri tek bir metot içinde gerçekleştirmeye karar verdim ve Closure’u tercih ettim.

Kategori Yapısının Sebepleri

Bu kategori yapısını, ekstra bir Laravel/PHP paketi kullanmadan ve veritabanı tablo yapısını basit tutmak amacıyla oluşturdum. Alternatif olarak, Laravel’de hiyerarşik kategori paketi olan Baum’u kullanabilirdim, ancak bu durumda ek bir bağımlılığın getireceği potansiyel karmaşıklığı tercih etmedim.

Bu yaklaşımın kısa vadede hızlı ve basit bir çözüm sağladığını, ancak uzun vadede ve artan web trafiğiyle birlikte sorgu optimizasyonu ihtiyacını doğurabileceğini unutmamak önemli. Ancak, bu potansiyel sorun, önbellek kullanımıyla çözülebilir.

Çalışma Şekli:

<?php

public function show(Category $category)
{
    $category->load('categories.categories.categories');
    $ids = $category->getChildCategoryIDs();
    $products = Product::whereIn('category_id', $ids)->get();

    return view('products.index', compact('category', 'products'));
}

CategoryController içerisindeki show metodu, belirli bir kategoriyi ve o kategorinin ürünlerini listeleyen bir işlem gerçekleştirir. ‘Lazy load’ yöntemi kullanarak, hedef kategorinin alt kategorilerini (burada üç dal alıyoruz) elde ederiz ve model içinde tanımladığımız getChildCategoryIDs metodu ile bu alt kategorilerin ID’lerini alırız. Son olarak, bu ID’lere sahip tüm ürünlerin bir listesini WHERE IN sorgusu ile elde ederiz.

Kategoriler
Teknik

JavaScript’te Reaktiviteye Pratik Alternatif: Alpine.js

AlpineJS, HTML içinde kullanabilen, reaktif olarak DOM işlemleri yapabileceğiniz ufak bir framework.

Şunu sayfaya dahil edip başlayabilirsiniz:

<script src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.js" defer></script>
  1. Örnek:
<div x-data="{message: 'Hello world!'}">
  <h1 x-text="message"></h1>
  <input type="text" x-model="message">
</div>
  1. Örnek:

    <div x-data="{items: ['first', 'second']}">
    <template x-for="item in items" :key="item">
        <div>
            <span x-text="item"></span>
        </div>
    </template>
    </div>
  2. Örnek:

    <div x-data="{open: false, toggle() { this.open = !this.open }}">
    <button @click="toggle()">
        <span x-text="open ? 'Kapat' : 'Aç'"></span>
    </button>
    <div x-show="open">
        Görüntülenecek olan içerik
    </div>
    </div>

Daha detaylı kullanımına dökümantasyonundan göz atabilirsiniz. Bazı eksiklikleri var yeni olmasından dolayı ama bu haliyle bile birçok şeye çözüm olabiliyor.