Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Laravel admin araçlarında sürükle-bırak sıralama, göründüğünden daha hızlı karmaşık hale geliyor.
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Laravel admin araçlarında sürükle-bırak sıralama, göründüğünden daha hızlı karmaşık hale geliyor.

Yazılım

Laravel admin araçlarında sürükle-bırak sıralama, göründüğünden daha hızlı karmaşık hale geliyor.

teknomers
Son güncelleme: 5 Haziran 2026 15:31
teknomers
Paylaş
Paylaş

Yönetici arayüzlerindeki sürükle-bırak sıralama özellikleri çoğu zaman bir UI geliştirmesi olarak satılmaktadır. Pratikte ise genellikle bir veri modeline ilişkin bir karar, şatafat olarak gizlenmiştir.

<p>Bunu zor yoldan öğrendim. İlk versiyon her zaman ucuz hissedilir: bir sürükleme tutamağı ekle, ID'lerin bir dizisini gönder, bir <code>position</code> sütununu güncelle, tamam. Herkes etkileşimin görünür ve tatmin edici olduğundan dolayı üretken hissetmekte. Fakat gerçek sorular bu noktada ortaya çıkar. Tam olarak ne sıralanıyor? İki yönetici aynı anda sıralama yaparsa ne olur? Sıralama genel mi yoksa sınırlı mı? Birinci sayfa sıralama sonrası hâlâ bir anlam ifade ediyor mu? Destek, kimin değiştirdiğini ve nedenini açıklayabilir mi? Klavye kullanıcısı arayüzle savaşmadan aynı işi yapabilir mi?</p>

<p>Bu gizli maliyet. <strong>Sürükleyip bırakmanın zorluğu, tam sayıların yeniden sıralanmasında değil, sıralama, kapsam ve niyet hakkında ürününüzün gerçeğini tanımlamak zorunda kalmasındadır.</strong></p>

<p>Artık benim kuralım basit: Eğer sıralama birinci sınıf bir iş kavramı değilse, listeyi sürüklenebilir hale getirmeyin. Özellikle Laravel admin araçlarında, açık sıralama, sabitleme veya kapsamlı "taşıma" eylemleri genellikle serbest biçimli sıralamadan daha iyi sonuç verir.</p>

<h2>
  <a name="the-first-mistake-is-usually-conceptual-not-technical" href="#the-first-mistake-is-usually-conceptual-not-technical">
  </a>
  İlk hata genelde kavramsaldır, teknik değil
</h2>

<p>Çoğu ekip, sıralanabilir satırları nasıl uygulayacaklarını sormaya başlar. Bu zaten çok geçtir. Gerçek ilk soru: <strong>Bu sıralamanın sahibi olan tam olarak hangi koleksiyon?</strong></p>

<p>Eğer cevap belirsizse, veritabanı yalan söylemeye başlayacaktır.</p>

<p>Tipik bir admin ekranını ele alalım: makaleler, kullanıcılar, destek talepleri veya ürünler. Bir operatör bir tablo görür, belki durum veya arama ile filtrelenmiş, ve bir satırı diğerinin üstüne sürükler. UI, görünen listeyi yeniden sıraladıklarını ima eder. Fakat o liste tam olarak nedir?</p>

<p>Bu aşağıdakilerden hangisi?</p>

<ul>
    <li>tablodaki tüm kayıtlar</li>
    <li>bir kiracı içindeki kayıtlar</li>
    <li>bir kategori içindeki kayıtlar</li>
    <li>geçerli filtreye uyan kayıtlar</li>
    <li>sadece mevcut sayfadaki kayıtlar</li>
    <li>el ile oluşturulmuş bir editoryal koleksiyon içindeki kayıtlar</li>
</ul>

<p>Bunlar tamamen farklı sözleşmelerdir. Çoğu “hızlı” sürükle-bırak uygulaması global bir <code>position</code> saklar ve zor kısımları erteleyerek geçiştirir. Bu, kullanıcı arayüzü bir veri dilimini gösterdiği ve kullanıcı dilimin doğru olduğuna inandığında doğru çalışır.</p>

<p>Artık ilk aradığım başarısızlık modu budur. Filtreli bir listede ilk görünen kaydın, gerçek depolanmış dizideki <code>42</code> pozisyonu olabilir. Kullanıcı, o filtreli görünümde aşağıya sürüklediğinde, yerel sıralamayı değiştirdiğini düşünür. Sistem, aslında Asla dokunmamayı kastetmediği çok daha geniş bir genel sıralamayı yeniden yazabilir.</p>

<p>Bu nedenle artık sıralamayı bir sunum kaygısı olarak görmüyorum. Bu bir alan durumudur.</p>

<h3>
  <a name="order-only-behaves-well-when-the-scope-is-explicit" href="#order-only-behaves-well-when-the-scope-is-explicit">
  </a>
  Sıralama yalnızca kapsam belirgin olduğunda iyi davranır
</h3>

<p>Manuel sıralamanın kesinlikle geçerli olduğu durumlar vardır:</p>

<ul>
    <li>ana sayfa kahraman kartları</li>
    <li>navigasyon menüleri</li>
    <li>onboarding adımları</li>
    <li>çalma listesi öğeleri</li>
    <li>kanban kartları bir sütun içinde</li>
    <li>bir form oluşturucu içindeki özel alanlar</li>
</ul>

<p>Her bir durumda, sıralı setin net bir üst varlığı vardır. Sıra, iş için bir şey ifade eder. Kullanıcılar bu anlamı anlar. Liste genellikle tamamı açısından düşünülmesi yeterince küçüktür.</p>

<p>Bu, istediğiniz şekildir.</p>

<p>İyi bir kural, sıralanabilir bir kaydın bu cümleyi net bir şekilde yanıtlayabilmesi gerektiğidir:</p>

<p><strong>Ben, koleksiyon Z içinde, pozisyon Y'deki nesne X'im.</strong></p>

<p>Eğer sisteminiz Z’yi tam olarak dolduramıyorsa, muhtemelen sıralanabilir bir alanınız yoktur. Bir sıralanabilir UI illüzyonuyla karşı karşıyasınız.</p>

<h2>
  <a name="laravel-makes-the-happy-path-dangerously-cheap" href="#laravel-makes-the-happy-path-dangerously-cheap">
  </a>
  Laravel, mutlu yolu tehlikeli bir şekilde ucuzlaştırır
</h2>

<p>Laravel, CRUD özelliklerini yürütmekte mükemmel bir iş çıkarmaktadır. Bu genelde bir güçlü yön. Ancak sürükle-bırak sıralamada, gerçek maliyeti gizleyebilir.</p>

<p>Kolay versiyon şu şekilde görünür:<br/></p>

<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="nc">Route</span><span class="o">::</span><span class="nf">post</span><span class="p">(</span><span class="s1">'/admin/articles/reorder'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="kt">Request</span> <span class="nv">$request</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">input</span><span class="p">(</span><span class="s1">'ids'</span><span class="p">,</span> <span class="p">[])</span> <span class="k">as</span> <span class="nv">$index</span> <span class="o">=&gt;</span> <span class="nv">$id</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">Article</span><span class="o">::</span><span class="nf">whereKey</span><span class="p">(</span><span class="nv">$id</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">update</span><span class="p">([</span><span class="s1">'position'</span> <span class="o">=&gt;</span> <span class="nv">$index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">noContent</span><span class="p">();</span>
<span class="p">});</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
    <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
    </svg>

    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
    <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
    </svg>

</div>
</div>

<p>Bu kod kısa, okunabilir ve çoğu karmaşık admin sistemi için yanlıştır.</p>

<p>Aşağıdakileri belirtmeden varsaymaktadır:</p>

<ul>
    <li>istemcinin tam yetkili bir liste gönderdiği</li>
    <li>listenin bir sabit kapsamda bulunduğu</li>
    <li>başka birinin o kapsamı eş zamanlı olarak değiştirmediği</li>
    <li>mevcut sayfanın önemli olan tüm diziyi gösterdiği</li>
    <li>her pozisyonu yeniden yazmanın kabul edilebilir olduğu</li>
    <li>denetlenebilirliğin önemsiz olduğu</li>
</ul>

<p>Bu döngü için çok fazla belirtilmemiş ürün mantığı var.</p>

<h3>
  <a name="a-safer-baseline-starts-in-the-schema" href="#a-safer-baseline-starts-in-the-schema">
  </a>
  Daha güvenli bir temel, şemada başlar
</h3>

<p>Eğer sıra önem taşıyorsa, bunu veritabanında kapsamlı bir sıraya göre tanımlayın.<br/></p>

<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="nc">Schema</span><span class="o">::</span><span class="nf">create</span><span class="p">(</span><span class="s1">'playlist_items'</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="kt">Blueprint</span> <span class="nv">$table</span><span class="p">)</span> <span class="p">{</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">id</span><span class="p">();</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">foreignId</span><span class="p">(</span><span class="s1">'playlist_id'</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">constrained</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">cascadeOnDelete</span><span class="p">();</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">foreignId</span><span class="p">(</span><span class="s1">'track_id'</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">constrained</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">cascadeOnDelete</span><span class="p">();</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">unsignedInteger</span><span class="p">(</span><span class="s1">'position'</span><span class="p">);</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">unsignedInteger</span><span class="p">(</span><span class="s1">'order_version'</span><span class="p">)</span><span class="o">-&gt;</span><span class="k">default</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">timestamps</span><span class="p">();</span>

    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">unique</span><span class="p">([</span><span class="s1">'playlist_id'</span><span class="p">,</span> <span class="s1">'position'</span><span class="p">]);</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">unique</span><span class="p">([</span><span class="s1">'playlist_id'</span><span class="p">,</span> <span class="s1">'track_id'</span><span class="p">]);</span>
    <span class="nv">$table</span><span class="o">-&gt;</span><span class="nf">index</span><span class="p">([</span><span class="s1">'playlist_id'</span><span class="p">,</span> <span class="s1">'position'</span><span class="p">]);</span>
<span class="p">});</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
    <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
    </svg>

    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
    <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
    </svg>

</div>
</div>

<p>Bu şema, değerli bir şey ifade eder:</p>

<ul>
    <li><code>position</code> küresel olarak anlamlı değildir</li>
    <li>çarpışmalar üst kapsam içinde önlenir</li>
    <li>okuma işlemlerinin stabil bir indeksi vardır</li>
    <li>eş zamanlılık, <code>order_version</code> aracılığıyla düşünülebilir</li>
</ul>

<p>Bu, pek çok belirsizliği ortadan kaldırır.</p>

<h3>
  <a name="the-write-path-should-validate-the-scope-it-mutates" href="#the-write-path-should-validate-the-scope-it-mutates">
  </a>
  Yazma yolunun, değiştirdiği kapsamı doğrulaması gerekir
</h3>

<p>Tam liste sıralama taleplerini kabul ettiğimde, sunucunun yükün gerçekten mevcut kısıtlı listeyle eşleştiğini kanıtlamasını isterim.<br/></p>

<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="k">final</span> <span class="kd">class</span> <span class="nc">ReorderPlaylistItems</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">handle</span><span class="p">(</span><span class="kt">Playlist</span> <span class="nv">$playlist</span><span class="p">,</span> <span class="kt">array</span> <span class="nv">$orderedIds</span><span class="p">,</span> <span class="kt">User</span> <span class="nv">$actor</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="no">DB</span><span class="o">::</span><span class="nf">transaction</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$playlist</span><span class="p">,</span> <span class="nv">$orderedIds</span><span class="p">,</span> <span class="nv">$actor</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$items</span> <span class="o">=</span> <span class="nv">$playlist</span><span class="o">-&gt;</span><span class="nf">items</span><span class="p">()</span>
                <span class="o">-&gt;</span><span class="nf">select</span><span class="p">([</span><span class="s1">'id'</span><span class="p">,</span> <span class="s1">'position'</span><span class="p">])</span>
                <span class="o">-&gt;</span><span class="nf">lockForUpdate</span><span class="p">()</span>
                <span class="o">-&gt;</span><span class="nf">orderBy</span><span class="p">(</span><span class="s1">'position'</span><span class="p">)</span>
                <span class="o">-&gt;</span><span class="nf">get</span><span class="p">();</span>

            <span class="nv">$expectedIds</span> <span class="o">=</span> <span class="nv">$items</span><span class="o">-&gt;</span><span class="nf">pluck</span><span class="p">(</span><span class="s1>'id'</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">values</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">all</span><span class="p">();</span>
            <span class="nv">$incomingIds</span> <span class="o">=</span> <span class="nb">array_values</span><span class="p">(</span><span class="nv">$orderedIds</span><span class="p">);</span>

            <span class="k">if</span> <span class="p">(</span><span class="nv">$incomingIds</span> <span class="o">!==</span> <span class="nv">$expectedIds</span> <span class="o">&amp;&amp;</span> <span class="nb">array_diff</span><span class="p">(</span><span class="nv">$expectedIds</span><span class="p">,</span> <span class="nv">$incomingIds</span><span class="p">)</span> <span class="o">!==</span> <span{}`p`>[])</span> <span class="p">{</span>
                <span class="k">throw</span> <span class="nc">ValidationException</span><span class="o">::</span><span class="nf">withMessages</span><span class="p">([</span>
                    <span class="s1">'items'</span> <span class="o">=&gt;</span> <span class="s1>'Reorder payload does not match the current playlist scope.'</span><span class="p">,</span>
                <span class="p">]);</span>
            <span class="p">}</span>

            <span class="k">foreach</span> <span class="p">(</span><span class="nv">$incomingIds</span> <span class="k">as</span> <span class="nv">$index</span> <span class="o">=&gt;</span> <span class="nv">$id</span><span class="p">)</span> <span class="p">{</span>
                <span class="nv">$playlist</span><span class="o">-&gt;</span><span class="nf">items</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">whereKey</span><span class="p">(</span><span class="nv">$id</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">update</span><span class="p">([</span>
                    <span class="s1>'position'</span> <span class="o">=&gt;</span> <span class="nv">$index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span>
                <span class="p">]);</span>
            <span class="p">}</span>

            <span class="nv">$playlist</span><span class="o">-&gt;</span><span class="nf">increment</span><span class="p">(</span><span class="s1>'order_version'</span><span class="p">);</span>

            <span class="nf">activity</span><span class="p">()</span>
                <span class="o">-&gt;</span><span class="nf">performedOn</span><span class="p">(</span><span class="nv">$playlist</span><span class="p">)</span>
                <span class="o">-&gt;</span><span class="nf">causedBy</span><span class="p">(</span><span class="nv">$actor</span><span class="p">)</span>
                <span class="o">-&gt;</span><span class="nf">withProperties</span><span class="p">([</span>
                    <span class="s1>'before'</span> <span class="o">=&gt;</span> <span class="nv">$items</span><span class="o">-&gt;</span><span class="nf">map</span><span class="p">(</span><span class="k">fn</span> <span class="p">(</span><span class="nv">$item</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1>'id'</span> <span class="o">=&gt;</span> <span class="nv">$item</span><span class="o">-&gt;</span><span class="n">id</span><span class="p">,</span> <span class="s1>'position'</span> <span class="o">=&gt;</span> <span class="nv">$item</span><span class="o">-&gt;</span><span class="n">position</span><span class="p">])</span><span class="o">-&gt;</span><span class="nf">all</span><span class="p">(),</span>
                    <span class="s1>'after'</span> <span class="o">=&gt;</span> <span class="nf">collect</span><span class="p">(</span><span class="nv">$incomingIds</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">values</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">map</span><span class="p">(</span><span class="k">fn</span> <span class="p">(</span><span class="nv">$id</span><span class="p">,</span> <span class="nv">$index</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1>'id'</span> <span class="o">=&gt;</span> <span class="nv">$id</span><span class="p">,</span> <span class="s1>'position'</span> <span class="o">=&gt;</span> <span class="nv">$index</span><span class="o">+</span> <span class="mi">1</span><span class="p">])</span><span class="o">-&gt;</span><span class="nf">all</span><span class="p">(),</span>
                <span class="p">])</span>
                <span class="o">-&gt;</span><span class="nb">log</span><span class="p">(</span><span class="s1>'playlist_reordered'</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
    <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
    </svg>

    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
    <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
    </svg>

</div>
</div>

<p>Burada, gerçek yeniden indekslemenin etrafında ne kadar iş olduğunu görün: </p>

<ul>
    <li>satır kilitleme</li>
    <li>yük doğrulama</li>
    <li>kapsam doğrulama</li>
    <li>sürüm artırma</li>
    <li>audit işlemleri</li>
</ul>

<p>Bu, kod biçimindeki gizli maliyet. Yeniden sıralama mantığı kolaydır. Etrafındaki her şey ise özelliktir.</p>

<p>Laravel’in veritabanı ve sayfalama belgeleri burada önemlidir, çünkü sıralı ve kısmi sonuç kümeleri ile çalışmayı oldukça kolay hale getirirler, ancak ürün seviyesindeki kararları kaldırmazlar: <a href="https://laravel.com/docs/12.x/database" target="_blank" rel="noopener noreferrer">https://laravel.com/docs/12.x/database</a> ve <a href="https://laravel.com/docs/12.x/pagination" target="_blank" rel="noopener noreferrer">https://laravel.com/docs/12.x/pagination</a>.</p>

<h2>
  <a name="concurrency-is-where-simple-sorting-stops-being-simple" href="#concurrency-is-where-simple-sorting-stops-being-simple">
  </a>
  Eş zamanlılık, “basit sıralama” işleminin basitliği sona erer
</h2>

<p>Sıralanabilir bir liste tek kullanıcı demolarında sorun olmaz. Ancak yönetici sistemleri nadiren tek kullanıcılıdır.</p>

<p>İki operatör aynı koleksiyonu ilk kez kullanmaya başladıklarında, varsayımlarınız test edilir. Bir kişi A öğesini en üstte taşır. Diğeri C öğesini D öğesinden aşağıya taşır. Her iki eylem de mantıklıdır. Her ikisi de geçerli yazımlar üretebilir. Ancak birinin, uygulamanın niyetini görmezden geldiğini hissedecektir.</p>

<p>Bu, özelliğin bir eşzamanlılık hikayesine değil, sadece bir kontrolör eylemine ihtiyaç duyduğu anlamına gelir.</p>

<h3>
  <a name="lastwritewins-is-simple-and-usually-bad" href="#lastwritewins-is-simple-and-usually-bad">
  </a>
  Son yazma kazanır, bu basit ve genelde kötüdür
</h3>

<p>Pek çok uygulamadaki varsayılan davranış son yazmayı kazanmak şeklindedir. Kim daha sonra gönderirse, ilk sırayı sessizce yeniden yazar.</p>

<p>Bu kolay bir şekilde uygulanabilir ve operatör güveni için korkunç bir durum. Üç tane destek problemi yaratır:</p>

<ul>
    <li>kullanıcılar arayüzün hatalı olduğunu düşünür</li>
    <li>yönetici, neden sıralamanın beklenmedik bir şekilde değiştiğini açıklayamaz</li>
    <li>denetim, geçerli bir yazım gösterse de kaybolan niyeti göstermez</li>
</ul>

<p>Önemli admin iş akışları için sessiz yeniden yazma, tarafsız bir seçim değildir. Bu, bir ürün borcudur.</p>

<h3>
  <a name="revisionbased-rejection-is-boring-and-correct" href="#revisionbased-rejection-is-boring-and-correct">
  </a>
  Revizyon bazlı reddetme sıkıcıdır ve doğrudur
</h3>

<p>Kullandığım en dürüst desen, bir sıra sürüm ile iyimser eşzamanlılıktır.</p>

<p>Müşteriye mevcut <code>order_version</code> liste ile birlikte verilir. Yeniden sıralama isteği bunu geri gönderir. Eğer saklanan sürüm değiştiyse, sunucu <code>409 Conflict</code> ile reddeder ve UI yeniden yüklenir.<br/></p>

<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="k">final</span> <span class="kd">class</span> <span class="nc">ReorderPlaylistRequest</span> <span class="kd">extends</span> <span class="nc">FormRequest</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">rules</span><span class="p">():</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="p">[</span>
            <span class="s1>'ordered_ids'</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1>'required'</span><span class="p">,</span> <span class="s1>'array'</span><span class="p">],</span>
            <span class="s1>'ordered_ids.*'</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1>'integer'</span><span class="p">],</span>
            <span class="s1>'version'</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1>'required'</span><span class="p">,</span> <span class="s1>'integer'</span><span class="p">],</span>
        <span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">final</span> <span class="kd">class</span> <span class="nc">ReorderPlaylistController</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">__invoke</span><span class="p">(</span>
        <span class="kt">ReorderPlaylistRequest</span> <span class="nv">$request</span><span class="p">,</span>
        <span class="kt">Playlist</span> <span class="nv">$playlist</span><span class="p">,</span>
        <span class="kt">ReorderPlaylistItems</span> <span class="nv">$service</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="nf">abort_if</span><span class="p">(</span><span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">integer</span><span class="p">(</span><span class="s1>'version'</span><span class="p">)</span> <span class="o">!==</span> <span class="nv">$playlist</span><span class="o">-&gt;</span><span class="n">order_version</span><span class="p">,</span> <span class="mi">409</span><span class="p">,</span> <span class="s1>'This list changed. Reload and try again.'</span><span class="p">);</span>

        <span class="nv">$service</span><span class="o">-&gt;</span><span class="nf">handle</span><span class="p">(</span><span class="nv">$playlist</span><span class="p">,</span> <span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">integer</span><span class="p">(</span><span class="s1>'ordered_ids'</span><span class="p">),</span> <span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">user</span><span class="p">());</span>

        <span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">json</span><span class="p">([</span><span class="s1>'ok'</span> <span class="o">=&gt;</span> <span class="kc">true</span><span class="p">,</span> <span class="s1>'version'</span> <span class="o">=&gt;</span> <span class="nv">$playlist</span><span class="o">-&gt;</span><span class="nf">fresh</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">order_version</span><span class="p">]);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
    <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
    </svg>

    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
    <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
    </svg>

</div>
</div>

<p>Bunlar, gerçek yeniden sıralama mantığının etrafında ne kadar iş olduğuna dikkat çekiyor:</p>

<ul>
    <li>satır kilitleme</li>
    <li>yük doğrulama</li>
    <li>kapsam doğrulama</li>
    <li>sürüm artırma</li>
    <li>denetim kayıtları</li>
</ul>

<p>Bunun yanında, yeniden sıralama mantığı kolaydır, etrafındaki iş daha karmaşıktır. Laravel’in veritabanı ve sayfalama belgeleri uygun sıradaki ve kısmi sonuç kümesi ile çalışmayı çok kolaylaştırmaktadır. Ancak, ürün düzeyindeki kararları kaldırmamaktadır: <a href="https://laravel.com/docs/12.x/database" target="_blank" rel="noopener noreferrer">https://laravel.com/docs/12.x/database</a> ve <a href="https://laravel.com/docs/12.x/pagination" target="_blank" rel="noopener noreferrer">https://laravel.com/docs/12.x/pagination</a>.</p>

<h2>
  <a name="pagination-is-usually-the-point-where-the-feature-becomes-dishonest" href="#pagination-is-usually-the-point-where-the-feature-becomes-dishonest">
  </a>
  Sayfalama genelde özelliğin güvenilirliğini zedelediği noktadır
</h2>

<p>Kendi görüşüm: <strong>Bir listenin sayfalandırılması gerekiyorsa, sürükleyip bırakma muhtemelen yanlış bir varsayımdır.</strong></p>

<p>Her zaman değil, ama genellikle.</p>

<p>Neden sadece teknik zorluk? Kullanıcı beklentisi de önemli bir etken.</p>

<p>Biri satırları sürüklediğinde, görünür bütünlüğü manipüle ettiğini varsayar. Sayfalama, ona bunun yalnızca bir dilim olduğunu söyler. Bu iki zihinsel model birbiriyle savaşıyor.</p>

<h3>
  <a name="the-real-questions-pagination-creates" href="#the-real-questions-pagination-creates">
  </a>
  Sayfalamanın oluşturduğu gerçek sorular
</h3>

<p>Diyelim ki bir admin tablosu, sayfa başına 25 satır gösteriyor.</p>

<p>Kullanıcı, sayfa 1'deki 25. satırı en üste sürüklüyorsa:</p>

<ul>
    <li>26. satır artık sayfa 1'e mi taşınmalı</li>
    <li>ikinci sayfa canlı olarak yeniden şekillenecek mi</li>
    <li>kullanıcı, global sıralamayı mı yoksa sayfa yerel sıralamasını mı düzenliyor</li>
    <li>filtreli sıralama durumunda ne olur</li>
    <li>alt sıraya taşımak tam olarak ne anlama geliyor, tüm diziyi yüklemeden</li>
</ul>

<p>Bu sorular kozmetik değil, güvenilirliğini belirlerler.</p>

<p>Yaygın çözüm, yalnızca mevcut sayfa içinde sürüklemeye izin vermektir. Bu görünüşte pragmatik görünse de çoğu zaman daha kötü bir yanlışa neden olur. UI, genel sıralama gibi görünür, ancak davranış aslında gizli bir genel dizinin karşısında sayfa yerel değişikliktir.</p>

<p>Bu, tam olarak bir sahneleme demosunda makul görünen ancak operatörler için aylarca kafa karıştırıcı olan bir özelliğe yol açar.</p>

<h3>
  <a name="better-patterns-for-large-admin-collections" href="#better-patterns-for-large-admin-collections">
  </a>
  Büyük admin koleksiyonları için daha iyi desenler
</h3>

<p>Eğer koleksiyon, tamamı rahatlıkla görüntülenemeyecek kadar büyükse, genellikle şunlardan birini tercih ederim:</p>

<ul>
    <li><code>move up</code> ve <code>move down</code> kontrolü ile ince ayar</li>
    <li><code>pin to top</code> ve <code>unpin</code> ile öne çıkan içerik</li>
    <li>örneğin <code>featured</code>, <code>standard</code>, <code>archived</code>, <code>hidden</code> gibi kategoriler</li>
    <li>gerçekten sekansı yöneten kullanıcılar için açık sayısal sıralama girişleri</li>
    <li>manuel sıralamanın yalnızca sabit bir kompozit sıralamada tek bir sinyal olduğu ağırlıklı sıralama</li>
</ul>

<p>Bu desenler sürükle-bırak kadar büyülü görünmeyebilir. Ayrıca, açıklaması daha kolay, denetimi daha basit ve veritabanının sahte hassasiyeti temsil etme olasılığı çok daha düşüktür.</p>

<h3>
  <a name="exports-make-the-mismatch-worse" href="#exports-make-the-mismatch-worse">
  </a>
  İhracatlar yanlışlıkları daha da kötüleştirir
</h3>

<p>İhracat CSV'leri, API beslemeleri veya alt işlerin aynı sıralı veri kümesine bağımlı hale geldiği andan itibaren, yeniden sıralama özelliğiniz yalnızca bir UI kolaylığı olmaktan çıkmaktadır.</p>

<p>Eğer sıralama, ihracatları etkiliyorsa, sorular daha keskin hale gelir:</p>

<ul>
    <li>ihracat sıralaması global mi yoksa filtrelenmiş mi</li>
    <li>geçici bir admin görünümü müşteri önünde görünen sıralamayı değiştirir mi</li>
    <li>peş peşe ihraçlar, bir operatör bir satırı sürüklediği için farklılık gösterir mi</li>
    <li>alt sistem kullanıcıları, bu sıralamayı iş önceliği olarak mı kullanıyor</li>
</ul>

<p>Bu noktada, ekipler sıralamayı politikaya dönüştürme tehlikesi taşır. El ile ayarlanmış bir admin sıralaması, asla bağımlı olmayan sistemler için görünmez bir doğru kaynağına dönüşebilir.</p>

<p>Eğer bu gerçekten iş gereksinimi ise, tamam. Ancak bunu ciddiye alarak ele alın. Eğer değilse, sıralanabilir bir ızgara, ürün ekibinin niyet ettiğinden daha fazla gerçeği tanımlamasına izin vermeyin.</p>

<h2>
  <a name="auditability-and-accessibility-are-not-edge-cases" href="#auditability-and-accessibility-are-not-edge-cases">
  </a>
  Denetim ve erişilebilirlik kenar durumları değildir
</h2>

<p>Ekipler sürükle-bırakın “çalıştığını” söylediğinde genellikle tahtaların hareket ettiğini ve isimlendirilmesinin yeterli olduğunu kastederler. Admin araçlarında bu yeterli değildir.</p>

<h3>
  <a name="if-order-matters-the-change-must-be-explainable-later" href="#if-order-matters-the-change-must-be-explainable-later">
  </a>
  Eğer sıralama önemliyse, değişikliğin daha sonra açıklanabilir olması gerekir
</h3>

<p>Birisi bir gün şunları soracaktır:</p>

<ul>
    <li>sıralama değişikliğini kim yaptı</li>
    <li>ne zaman değişti</li>
    <li>önceki sıralama neydi</li>
    <li>değişiklik kasıtlı mıydı</li>
    <li>operatör yalnızca bir alt küme mi değiştirmek istedi</li>
</ul>

<p>Basit bir <code>position</code> sütunu bunların hiçbiri için yanıt veremez. Eğer sıralama personelin veya müşterilerin gördüğü şeyleri etkiliyorsa, yeniden sıralama olaylarını olaylar olarak kaydedin, sadece satır farklılıkları olarak değil. Aktörü, kapsamı, önceki durumu, mümkünse sonraki durumu ve istek bağlamını kaydedin.</p>

<p>Ayrıca, niyetin açık olduğu değişiklikleri tercih ederim. “Ana sayfa bölümünde Fiyatlandırma kartını SSS’nin üzerinde kaydırdı” anlamlı bir denetim olayıdır. “47 konum değerini güncelledi” ise değildir.</p>

<h3>
  <a name="keyboard-access-changes-the-product-design-in-a-good-way" href="#keyboard-access-changes-the-product-design-in-a-good-way">
  </a>
  Klavye erişimi ürün tasarımını olumlu bir şekilde değiştirir
</h3>

<p>Birçok sürükle-bırak arayüzü, ilk başta işaretçi odaklı ve erişilebilirliği ikinci planda tutar. Bu, bir ürün kokusudur.</p>

<p>Eğer sıralanabilir bir görev önemliyse, tamamlayıcı bir işaretçi yolu gerektirir. Pratikte, bu genellikle şunları içerir:</p>

<ul>
    <li>en üste taşı</li>
    <li>yukarı taşı</li>
    <li>aşağı taşı</li>
    <li>en alta taşı</li>
    <li>seçilen öğenin önüne taşı</li>
    <li>seçilen öğenin arkasına taşı</li>
</ul>

<p>Takımlar bazen bunu bir uyum takviyesi olarak değerlendirir. Bunu tersine çevirmeyi düşünmüyorum. Bu komutlar iyi tasarlandığında, tüm özellik gelişiyor. Niyet açık hale geliyor. Hassasiyet artıyor. Destek daha net bir dil kazanıyor. Denetimlerin anlaşılması daha kolay hale geliyor.</p>

<p>Bu, biçimsiz sürüklemenin çok fazla teatral iş yaptığını ve yeterince operasyonel iş yapmadığını gösteren en güçlü sinyallerden biridir.</p>

<h2>
  <a name="what-i-ship-instead-in-most-laravel-admin-tools" href="#what-i-ship-instead-in-most-laravel-admin-tools">
  </a>
  Çoğu Laravel admin aracında neyi tercih ediyorum
</h2>

<p>Benim varsayılan üretim modelim artık “sıralanabilir satırlar” değil. <strong>Açık komutlarla kapsamlı sıralama</strong>.</p>

<p>Şekil genellikle şudur:</p>

<ol>
    <li>bir üst kapsam net bir şekilde tanımlayın</li>
    <li>bir nullable <code>manual_rank</code> veya kapsamlı <code>position</code> depolayın</li>
    <li>bunu sabit bir ikincil sıralama ile birleştirin, örneğin <code>created_at</code>, <code>name</code> veya iş önceliği</li>
    <li>sınırsız sürüklemeler yerine açıkça eylemleri ifşa edin</li>
    <li>paylaşılan admin durumunu etkileyen her yeniden sıralama işlemini denetleyin</li>
</ol>

<p>Örneğin, pratik bir sorgu genellikle şöyle görünmektedir:<br/></p>

<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="nv">$articles</span> <span class="o">=</span> <span class="nc">Article</span><span class="o">::</span><span class="nf">query</span><span class="p">()</span>
    <span class="o">-&gt;</span><span class="nf">where</span><span class="p">(</span><span class="s1>'tenant_id'</span><span class="p">,</span> <span class="nv">$tenant</span><span class="o">-&gt;</span><span class="n">id</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">orderByRaw</span><span class="p">(</span><span class="s1>'CASE WHEN manual_rank IS NULL THEN 1 ELSE 0 END'</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">orderBy</span><span class="p">(</span><span class="s1>'manual_rank'</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">orderByDesc</span><span class="p">(</span><span class="s1>'published_at'</span><span class="p">)</span>
    <span class="o">-&gt;</span><span class="nf">paginate</span><span class="p">(</span><span class="mi">25</span><span class="p">);</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
    <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
    </svg>

    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
    <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
    </svg>

</div>
</div>

<p>Bu model çok daha dürüst. Sıralanmış ögeler, işin açık şekilde yerleştirdiği yerde yüzer. Sıralanmamış öğeler hâlâ tahmin edilebilir şekilde davranır. Sayfalandırma anlaşılır kalır. “Sabitle,” “yukarı taşı” veya doğrudan sıralama düzenlemeleri eklemek mümkündür, böylece her kayıt bir kutsal toplam sıralamada varmış gibi yapmaya gerek kalmaz.</p>

<h3>
  <a name="when-i-still-allow-draganddrop" href="#when-i-still-allow-draganddrop">
  </a>
  Hala sürükleyip bırakmaya ne zaman izin veriyorum
</h3>

<p>Tüm bunlar göz önünde bulundurulduğunda, hala şu dar alanlarda kullanıyorum:</p>

<ul>
    <li>liste küçük</li>
    <li>tam liste görünür</li>
    <li>kapsam net</li>
    <li>sıralama, bir iş nesnesi olarak önem taşıyor</li>
    <li>eşzamanlılık çatışmaları kabul edilebilir veya açıkça ele alınıyor</li>
    <li>denetim olabilir</li>
    <li>klavye alternatifleri mevcuttur</li>
</ul>

<p>Bu, genellikle editoryal ve yapı oluşturma tarzı arayüzlerdir, geniş CRUD tabloları değildir.</p>

<p>Eğer özelliğiniz bu testlerden iki veya üçünü geçmiyorsa, daha fazla JavaScript ve frontend kütüphanesine dair daha güçlü görüşler ile telafi etmeye çalışmayın. Problemi oluşturan muhtemelen anlaşmadır, sürükleme tutamağı değil.</p>

<p>Pratikte alınacak ders açık olmalıdır çünkü gerekmektedir. <strong>Sadece sezgisel göründüğü için sürükle-bırak sıralama eklemeyin. Sadece sıralanan koleksiyon gerçek, sınırlı, denetlenebilir ve bütünü olarak mantık yürütulebilecek kadar küçük olduğunda ekleyin.</strong> Diğer tüm durumlarda, açık sıralama, teatral sıralamayı geçer ve veritabanınız daha az yalan söyleyecektir.</p>

<hr/>

<p>Bu yazının tamamını QCode’da okuyun: <a href="https://qcode.in/the-hidden-cost-of-adding-drag-and-drop-ordering-to-admin-tools/" target="_blank" rel="noopener noreferrer">https://qcode.in/the-hidden-cost-of-adding-drag-and-drop-ordering-to-admin-tools/</a></p>

Kaynak: Orijinal Makale

Gün 9: Dosya Yüklemeleri Beklediğimden Daha Tehlikeli
10 Dakikada Yatırım Web Sitesi Nasıl Başlatılır (Yönetici Paneli ile Ücretsiz HYIP Scripti)
Yılda 1 $’ın Altında Web Uygulama Hosting’i (15.000 IDR) Cloudflare Tüneli ile
Gizli Kalmış 5 Laravel 13 Özelliği: Bugün Kullanmayı Değebilir
Filament v5’te Kaynak Oluşturma | Laravel Kişisel Finans Panosu
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Renkli Yedek Parçalarla MacBook Neo’yu Kişiselleştirdim
Sonraki Makale Acil: SOC’ların Sadece %10’u AI’dan Mükemmel Değer Aldığını Söylüyor

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Acil: Microsoft Haziran 2026 Yamanı 3 Sıfır Gün Açığı ve 200 Hata Düzeltiyor
Siber Güvenlik
MacOS 27 Golden Gate: Yeni Özellikler ve Öne Çıkan Yenilikler
Genel
CISA, FBI ve Devlet Kurumlarına VPN Açığını Üç Günde Kapatma Talimatı Verdi
Genel
Fitbit Charge 6 ve Ace LTE Yeni $100’lık Air ile Aynı Fiyatta
Liste
WWDC 2026: Siri AI, iOS 27 ve Apple İnovasyonları Açıklandı
Yapay Zeka
Razer Seiren V3 Pro İncelemesi: USB, XLR ve 32-bit akış
Donanım
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?