<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Doğukan Öksüz</title>
        <link>https://dogukan.dev</link>
        <description>Merhaba, ben Doğukan Öksüz. Websitemde sizlere yazılım dünyasından ve linuxtan bahsettiğim yazılar yayınlıyorum. Ayrıca web developer olarak çalıştığımdan referanslarımı da inceleyebilirsiniz.</description>
        <lastBuildDate>Sat, 11 Apr 2026 14:14:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Next.js 13</generator>
        <image>
            <title>Doğukan Öksüz</title>
            <url>https://dogukan.dev/favicon/favicon.png</url>
            <link>https://dogukan.dev</link>
        </image>
        <copyright>All rights reserved 2026, Doğukan Öksüz</copyright>
        <item>
            <title><![CDATA[React Transfer Componenti Geliştirmek]]></title>
            <link>https://dogukan.dev/react-transfer-componenti-gelistirmek</link>
            <guid>https://dogukan.dev/react-transfer-componenti-gelistirmek</guid>
            <pubDate>Sat, 24 Jun 2023 12:32:33 GMT</pubDate>
            <description><![CDATA[Yakın zamanlarda React tarafına artan ilgimle birlikte şirkette geliştirdiğim Liman Merkezi Yönetim Sistemini modern bir görüntüye ve kolaylaştırılmış kullanıcı deneyimine kavuşturmak istedik.
Bu yenileme işlemini yaparken N...]]></description>
            <content:encoded><![CDATA[<p><span style="font-weight: 400;">Yakın zamanlarda React tarafına artan ilgimle birlikte şirkette geliştirdiğim Liman Merkezi Y&ouml;netim Sistemini modern bir g&ouml;r&uuml;nt&uuml;ye ve kolaylaştırılmış kullanıcı deneyimine kavuşturmak istedik.</span></p>
<p><span style="font-weight: 400;">Bu yenileme işlemini yaparken NextJS framework&uuml;n&uuml; ve trend olan Radix UI&rsquo;ın Tailwind formatına d&ouml;n&uuml;şt&uuml;r&uuml;lm&uuml;ş hali olan shadcn/ui componentlerini kullandık. Ancak bu component k&uuml;t&uuml;phanesinin i&ccedil;erisinde transfer componenti bulunmuyordu.</span></p>
<h2>&nbsp;</h2>
<h2><span style="font-weight: 400;">Transfer componenti nedir?</span></h2>
<p><span style="font-weight: 400;">Transfer componenti pasif/aktif olan iki tarafın tek ekranda g&ouml;r&uuml;nt&uuml;lenerek sergilenen itemların durumlarının hızlıca değiştirilmesine olanak sağlayan bir component t&uuml;r&uuml;d&uuml;r.</span></p>
<p><span style="font-weight: 400;">Biz bu componenti kullanıcılara atabilen yetkileri ve halihazırda atanmış yetkileri g&ouml;r&uuml;nt&uuml;leyip hızlıca değişiklik sağlayabileceği bir durumun kullanıcı deneyimini geliştirmek i&ccedil;in yaptık.</span></p>
<p><span style="font-weight: 400;"><img src="/images/1/6496a9a37a8d0.png" alt="React Transfer Component" width="1600" height="1145" /></span></p>
<p><span style="font-weight: 400;">Yukarıda g&ouml;rebileceğiniz &uuml;zere oluşturulmuş bir yetkiye hangi kullanıcının sahip olabileceğini ve hangi kullanıcıların halihazırda sahip olduğunu g&ouml;r&uuml;nt&uuml;leyebiliyor ve bu kullanıcıların sahiplik durumları arasında hızlıca değişiklik ger&ccedil;ekleştirebiliyoruz.</span></p>
<p>&nbsp;</p>
<h2><span style="font-weight: 400;">React Transfer componentini nasıl geliştirebilirim?</span></h2>
<p><span style="font-weight: 400;">Bu componentin geliştirilmesini ben Tailwind ve Typescript kullanarak anlatacağım. Ayrıca kullandığım k&uuml;t&uuml;phaneden gelen Checkbox, Button gibi inputları kendi k&uuml;t&uuml;phanenizdekiler ile değiştirmeniz gerekebilir.</span></p>
<h3><span style="font-weight: 400;">Kullanacağımız stateleri belirleyelim</span></h3>
<h4><span style="font-weight: 400;">checked</span></h4>
<p><span style="font-weight: 400;">Se&ccedil;ilmiş olduğumuz elemanları tutacağımız state. <em>Tip (T[])</em></span></p>
<h4><span style="font-weight: 400;">left</span></h4>
<p><span style="font-weight: 400;">Sol kısmımızda (se&ccedil;ilmemiş) olan elemanları tuttuğumuz state. <em>Tip (T[])</em></span></p>
<h4><span style="font-weight: 400;">right</span></h4>
<p><span style="font-weight: 400;">Sağ kısımda (se&ccedil;ilmiş) olan elemanları tuttuğumuz state. <em>Tip (T[])<br /><br /></em></span></p>
<h3><span style="font-weight: 400;">Kullanacağımız fonksiyonları oluşturalım</span></h3>
<h4><span style="font-weight: 400;">not</span></h4>
<p><span style="font-weight: 400;">a ve b olarak <em>T[]</em> tipinde iki arg&uuml;man alır, a&rsquo;nın b&rsquo;yi i&ccedil;ermeyen elemanlarını belirler.</span></p>
<pre><span style="font-weight: 400;">function not(a: T[], b: T[]) {<br />&nbsp; return a.filter((value) =&gt; b.indexOf(value) === -1)<br />}</span></pre>
<h4><span style="font-weight: 400;"><br />intersection</span></h4>
<p><span style="font-weight: 400;">a ve b olarak T[] tipinde iki arg&uuml;man alır, a&rsquo;nın b&rsquo;yi i&ccedil;eren elemanlarını belirler.</span></p>
<pre><span style="font-weight: 400;">function intersection(a: T[], b: T[]) {<br />&nbsp; return a.filter((value) =&gt; b.indexOf(value) !== -1)<br />}</span></pre>
<h4><span style="font-weight: 400;"><br />union</span></h4>
<p><span style="font-weight: 400;">a ve b olarak T[] tipinde iki arg&uuml;man alır, iki taraftaki elemanları sadece unique olacak şekilde birleştirir.</span></p>
<pre><span style="font-weight: 400;">function union(a: T[], b: T[]) {<br />&nbsp; return [...a, ...not(b, a)]<br />}</span></pre>
<h4><span style="font-weight: 400;"><br />numberOfChecked</span></h4>
<p><span style="font-weight: 400;">Verdiğimiz taraftaki itemların ka&ccedil;ının se&ccedil;ili olduğunu intersection kullanarak bulur.</span></p>
<pre><span style="font-weight: 400;">const numberOfChecked = (items: T[]) =&gt; intersection(checked, items).length</span></pre>
<h4><span style="font-weight: 400;"><br />handleToggle</span></h4>
<p><span style="font-weight: 400;">Bu fonksiyon işaretlediğimiz elemanın checked stateine eklenmesini ya da &ccedil;ıkarılması işlemini sağlar.</span></p>
<pre>const handleToggle = (value: T) =&gt; () =&gt; {<br />&nbsp; const currentIndex = checked.indexOf(value)<br />&nbsp; const newChecked = [...checked]<br /><br />&nbsp; if (currentIndex === -1) {<br />&nbsp; &nbsp; newChecked.push(value)<br />&nbsp; } else {<br />&nbsp; &nbsp; newChecked.splice(currentIndex, 1)<br />&nbsp; }<br /><br />&nbsp; setChecked(newChecked)<br />}</pre>
<h4><span style="font-weight: 400;"><br />handleToggleAll</span></h4>
<p><span style="font-weight: 400;">Belirttiğimiz taraftaki t&uuml;m itemların se&ccedil;ilmesini ya da se&ccedil;imlerinin kaldırılmasını sağlar.</span></p>
<pre>const handleToggleAll = (items: T[]) =&gt; () =&gt; {<br />&nbsp; if (numberOfChecked(items) === items.length) {<br />&nbsp; &nbsp; setChecked(not(checked, items))<br />&nbsp; } else {<br />&nbsp; &nbsp; setChecked(union(checked, items))<br />&nbsp; }<br />}</pre>
<h4><span style="font-weight: 400;"><br />leftChecked / rightChecked</span></h4>
<p><span style="font-weight: 400;">intersection fonksiyonumuzu kullanarak hangi itemların se&ccedil;ili olduğunu bulur.</span></p>
<pre><span style="font-weight: 400;">const leftChecked = intersection(checked, left)</span><br /><span style="font-weight: 400;">const rightChecked = intersection(checked, right)</span></pre>
<h4><span style="font-weight: 400;"><br />handleCheckedLeft / handleCheckedRight</span></h4>
<p><span style="font-weight: 400;">Orta kısımdaki butonlarımızın sağa ya da sola g&ouml;nderme işlemlerini yaptığımız fonksiyonlar b&uuml;t&uuml;n&uuml;d&uuml;r. Bir taraftaki itemlardan se&ccedil;ili olanları &ouml;b&uuml;r kısım ile birleştirir ve diğer taraftan kaldırma işlemlerini yaptıktan sonra checked stateinden kaldırır.</span></p>
<pre>const handleCheckedRight = () =&gt; {<br />&nbsp; setRight(right.concat(leftChecked))<br />&nbsp; setLeft(not(left, leftChecked))<br />&nbsp; setChecked(not(checked, leftChecked))<br />}<br /><br />const handleCheckedLeft = () =&gt; {<br />&nbsp; setLeft(left.concat(rightChecked))<br />&nbsp; setRight(not(right, rightChecked))<br />&nbsp; setChecked(not(checked, rightChecked))<br />}<br /><br /></pre>
<h3><span style="font-weight: 400;">View kısımlarımızı oluşturalım</span></h3>
<p><span style="font-weight: 400;">Bu component iki adet aynı kutudan ve bir flexbox i&ccedil;erisinde bulunan alt alta iki d&uuml;ğmeden oluşmaktadır. İlk aşamada sol ve sağ kısımlarımız aynı olduğundan tek bir component yazalım.</span></p>
<h4><span style="font-weight: 400;">Checkboxlu kutuların geliştirilmesi</span></h4>
<pre>// Itemlar ve başlığa ihtiyacımız mevcut<br />const customList = (items: T[], title: string) =&gt; {<br />&nbsp; return (<br />&nbsp; &nbsp; &lt;Card className="flex-1"&gt;<br />&nbsp; &nbsp; &nbsp; &lt;CardHeader&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; {/* Başlık propumuzu bu alanda kullanıyoruz */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;CardTitle&gt;{title}&lt;/CardTitle&gt;<br />&nbsp; &nbsp; &nbsp; &lt;/CardHeader&gt;<br />&nbsp; &nbsp; &nbsp; &lt;CardContent&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex items-center space-x-2"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {/* handleToggleAll fonksiyonumuzu bu alanda kullanarak t&uuml;m&uuml;n&uuml;n se&ccedil;ilebilmesini sağlıyoruz */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Checkbox<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id={`select_all_${title}`}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={handleToggleAll(items)}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; checked={<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; numberOfChecked(items) === items.length &amp;&amp; items.length !== 0<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disabled={items.length === 0}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Label<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; htmlFor={`select_all_${title}`}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; T&uuml;m&uuml;n&uuml; se&ccedil; {/* ka&ccedil; adet item se&ccedil;tiğimizi belirtiyoruz */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {items.length &gt; 0 &amp;&amp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `(${numberOfChecked(items)}/${items.length} se&ccedil;ili)`}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Label&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;Separator className="my-6" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;ScrollArea className="h-72"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex flex-col gap-4"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {/* hi&ccedil; item yoksa veya kalmadıysa boş state'in g&ouml;sterimi */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {items.length === 0 &amp;&amp; (<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex flex-col items-center justify-center gap-3"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;FolderOpen className="h-8 w-8 text-foreground/70" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;span className="text-sm font-medium leading-none text-foreground/70"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Veri yok<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/span&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )}<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {/* itemların checkboxları ve labelları koyularak se&ccedil;ilebilir şekilde listelenmesi */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {items.map((value) =&gt; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex items-center space-x-2" key={value.id}&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {/* işaretlendiğinde valuenun değerinin toggle edildiği durum */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Checkbox<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id={value.id}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={handleToggle(value)}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; checked={checked.indexOf(value) !== -1}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Label<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; htmlFor={value.id}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {value.name}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Label&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScrollArea&gt;<br />&nbsp; &nbsp; &nbsp; &lt;/CardContent&gt;<br />&nbsp; &nbsp; &lt;/Card&gt;<br />&nbsp; );<br />};</pre>
<h4><span style="font-weight: 400;"><br />T&uuml;m componentlerin bir araya getirilmesi</span></h4>
<pre>return (<br />&nbsp; &lt;div&gt;<br />&nbsp; &nbsp; &lt;div className="flex gap-5"&gt;<br />&nbsp; &nbsp; &nbsp; {/* Sol kısmın renderlanması */}<br />&nbsp; &nbsp; &nbsp; {customList(left, props.leftTitle)}<br /><br />&nbsp; &nbsp; &nbsp; &lt;div className="buttons flex flex-col items-center justify-center gap-3"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; {/* Sağda se&ccedil;ililerin sol tarafa taşınmasını sağlayan buton */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;Button<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; variant="outline"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={() =&gt; handleCheckedLeft()}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disabled={rightChecked.length === 0}<br />&nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ChevronLeft className="h-4 w-4" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/Button&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; {/* Solda se&ccedil;ililerin sağ tarafa taşınmasını sağlayan buton */}<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;Button<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; variant="outline"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={() =&gt; handleCheckedRight()}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disabled={leftChecked.length === 0}<br />&nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ChevronRight className="h-4 w-4" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/Button&gt;<br />&nbsp; &nbsp; &nbsp; &lt;/div&gt;<br /><br />&nbsp; &nbsp; &nbsp; {/* Sağ kısmın renderlanması */}<br />&nbsp; &nbsp; &nbsp; {customList(right, props.rightTitle)}<br />&nbsp; &nbsp; &lt;/div&gt;<br /><br />&nbsp; &nbsp; {/* Eğer tanımlandıysa kaydet fonksiyonu ile birlikte kaydet d&uuml;ğmesinin g&ouml;sterilmesi*/}<br />&nbsp; &nbsp; {props.onSave &amp;&amp; (<br />&nbsp; &nbsp; &nbsp; &lt;div className="mt-5 flex justify-end"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;Button onClick={() =&gt; props.onSave &amp;&amp; props.onSave(right)}&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Save className="mr-2 h-4 w-4" /&gt; Kaydet<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/Button&gt;<br />&nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; )}<br />&nbsp; &lt;/div&gt;<br />);</pre>
<p>&nbsp;</p>
<h2><span style="font-weight: 400;">Transfer componentinin tamamlanmış hali</span></h2>
<pre>interface ITransferListItem {<br />&nbsp; id: string<br />&nbsp; name: string<br />}<br /><br />interface ITransferListProps&lt;T extends ITransferListItem&gt; {<br />&nbsp; items: T[]<br />&nbsp; selected?: T[]<br />&nbsp; leftTitle: string<br />&nbsp; rightTitle: string<br />&nbsp; loading: boolean<br />&nbsp; onSave?: (items: T[]) =&gt; void<br />&nbsp; renderName?: keyof T<br />}<br /><br />const TransferList = &lt;T extends ITransferListItem&gt;(<br />&nbsp; props: ITransferListProps&lt;T&gt;<br />) =&gt; {<br />&nbsp; const [checked, setChecked] = useState&lt;T[]&gt;([])<br />&nbsp; const [left, setLeft] = useState&lt;T[]&gt;([])<br />&nbsp; const [right, setRight] = useState&lt;T[]&gt;([])<br />&nbsp; const [leftSearch, setLeftSearch] = useState&lt;string&gt;("")<br />&nbsp; const [rightSearch, setRightSearch] = useState&lt;string&gt;("")<br /><br />&nbsp; const handleSelectedProp = () =&gt; {<br />&nbsp; &nbsp; if (!props.selected) return<br /><br />&nbsp; &nbsp; // Remove selected items from left side<br />&nbsp; &nbsp; setLeft((prev) =&gt;<br />&nbsp; &nbsp; &nbsp; prev.filter(<br />&nbsp; &nbsp; &nbsp; &nbsp; (object1) =&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; !props.selected?.some((object2) =&gt; object1.id === object2.id)<br />&nbsp; &nbsp; &nbsp; )<br />&nbsp; &nbsp; )<br />&nbsp; &nbsp; setRight(props.selected)<br />&nbsp; }<br /><br />&nbsp; useEffect(() =&gt; {<br />&nbsp; &nbsp; setLeft(props.items)<br />&nbsp; &nbsp; handleSelectedProp()<br />&nbsp; }, [props.items, props.selected])<br /><br />&nbsp; const leftChecked = intersection(checked, left)<br />&nbsp; const rightChecked = intersection(checked, right)<br /><br />&nbsp; function not(a: T[], b: T[]) {<br />&nbsp; &nbsp; return a.filter((value) =&gt; b.indexOf(value) === -1)<br />&nbsp; }<br /><br />&nbsp; function intersection(a: T[], b: T[]) {<br />&nbsp; &nbsp; return a.filter((value) =&gt; b.indexOf(value) !== -1)<br />&nbsp; }<br /><br />&nbsp; function union(a: T[], b: T[]) {<br />&nbsp; &nbsp; return [...a, ...not(b, a)]<br />&nbsp; }<br /><br />&nbsp; const numberOfChecked = (items: T[]) =&gt; intersection(checked, items).length<br /><br />&nbsp; const handleToggle = (value: T) =&gt; () =&gt; {<br />&nbsp; &nbsp; const currentIndex = checked.indexOf(value)<br />&nbsp; &nbsp; const newChecked = [...checked]<br /><br />&nbsp; &nbsp; if (currentIndex === -1) {<br />&nbsp; &nbsp; &nbsp; newChecked.push(value)<br />&nbsp; &nbsp; } else {<br />&nbsp; &nbsp; &nbsp; newChecked.splice(currentIndex, 1)<br />&nbsp; &nbsp; }<br /><br />&nbsp; &nbsp; setChecked(newChecked)<br />&nbsp; }<br /><br />&nbsp; const handleToggleAll = (items: T[]) =&gt; () =&gt; {<br />&nbsp; &nbsp; if (numberOfChecked(items) === items.length) {<br />&nbsp; &nbsp; &nbsp; setChecked(not(checked, items))<br />&nbsp; &nbsp; } else {<br />&nbsp; &nbsp; &nbsp; setChecked(union(checked, items))<br />&nbsp; &nbsp; }<br />&nbsp; }<br /><br />&nbsp; const handleCheckedRight = () =&gt; {<br />&nbsp; &nbsp; setRight(right.concat(leftChecked))<br />&nbsp; &nbsp; setLeft(not(left, leftChecked))<br />&nbsp; &nbsp; setChecked(not(checked, leftChecked))<br />&nbsp; }<br /><br />&nbsp; const handleCheckedLeft = () =&gt; {<br />&nbsp; &nbsp; setLeft(left.concat(rightChecked))<br />&nbsp; &nbsp; setRight(not(right, rightChecked))<br />&nbsp; &nbsp; setChecked(not(checked, rightChecked))<br />&nbsp; }<br /><br />&nbsp; const customList = (<br />&nbsp; &nbsp; items: T[],<br />&nbsp; &nbsp; title: string,<br />&nbsp; &nbsp; search: string,<br />&nbsp; &nbsp; setSearch: (e: string) =&gt; void<br />&nbsp; ) =&gt; {<br />&nbsp; &nbsp; return (<br />&nbsp; &nbsp; &nbsp; &lt;Card className="flex-1"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;CardHeader&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;CardTitle&gt;{title}&lt;/CardTitle&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/CardHeader&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;CardContent&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex items-center space-x-2"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Checkbox<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id={`select_all_${title}`}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={handleToggleAll(items)}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; checked={<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; numberOfChecked(items) === items.length &amp;&amp; items.length !== 0<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disabled={items.length === 0}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Label<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; htmlFor={`select_all_${title}`}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; T&uuml;m&uuml;n&uuml; se&ccedil;{" "}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {items.length &gt; 0 &amp;&amp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `(${numberOfChecked(items)}/${items.length} se&ccedil;ili)`}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Label&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Separator className="my-6" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="search relative"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Input<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; placeholder="Arama..."<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="mb-6 h-8"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value={search}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onChange={(e) =&gt; setSearch(e.target.value)}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Search className="absolute right-2 top-2 h-4 w-4 text-foreground/70" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScrollArea className="h-72"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex flex-col gap-4"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {props.loading &amp;&amp; (<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {[...Array(10)].map((_, index) =&gt; (<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex gap-2" key={index}&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Skeleton className="h-4 w-4" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Skeleton className="h-4 w-full" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ))}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {!props.loading &amp;&amp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items.filter((item) =&gt; item.name.toLowerCase().includes(search))<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .length === 0 &amp;&amp; (<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex flex-col items-center justify-center gap-3"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;FolderOpen className="h-8 w-8 text-foreground/70" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;span className="text-sm font-medium leading-none text-foreground/70"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Veri yok<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/span&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {items<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .filter((item) =&gt; item.name.toLowerCase().includes(search))<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .map((value) =&gt; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="flex items-center space-x-2" key={value.id}&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Checkbox<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id={value.id}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={handleToggle(value)}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; checked={checked.indexOf(value) !== -1}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Label<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; htmlFor={value.id}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {props.renderName<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ? value[props.renderName]<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : value.name}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Label&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScrollArea&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/CardContent&gt;<br />&nbsp; &nbsp; &nbsp; &lt;/Card&gt;<br />&nbsp; &nbsp; )<br />&nbsp; }<br /><br />&nbsp; return (<br />&nbsp; &nbsp; &lt;div&gt;<br />&nbsp; &nbsp; &nbsp; &lt;div className="flex gap-5"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; {customList(left, props.leftTitle, leftSearch, setLeftSearch)}<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="buttons flex flex-col items-center justify-center gap-3"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Button<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; variant="outline"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={() =&gt; handleCheckedLeft()}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disabled={rightChecked.length === 0}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ChevronLeft className="h-4 w-4" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Button&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Button<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; variant="outline"<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onClick={() =&gt; handleCheckedRight()}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; disabled={leftChecked.length === 0}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ChevronRight className="h-4 w-4" /&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Button&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; {customList(right, props.rightTitle, rightSearch, setRightSearch)}<br />&nbsp; &nbsp; &nbsp; &lt;/div&gt;<br /><br />&nbsp; &nbsp; &nbsp; {props.onSave &amp;&amp; (<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;div className="mt-5 flex justify-end"&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Button onClick={() =&gt; props.onSave &amp;&amp; props.onSave(right)}&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Save className="mr-2 h-4 w-4" /&gt; Kaydet<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Button&gt;<br />&nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; &nbsp; &nbsp; )}<br />&nbsp; &nbsp; &lt;/div&gt;<br />&nbsp; )<br />}<br /><br />export default TransferList<br /><span style="font-weight: 400;"><br /></span></pre>
<h3><span style="font-weight: 400;">&Ouml;rnek kullanım senaryosu</span></h3>
<pre><span style="font-weight: 400;">&lt;TransferList<br />&nbsp; items={users}<br />&nbsp; selected={selected}<br />&nbsp; loading={loading}<br />&nbsp; leftTitle="Bu role sahip olmayan kullanıcılar"<br />&nbsp; rightTitle="Bu role sahip kullanıcılar"<br />&nbsp; onSave={onSave}<br />/&gt;<br /><br /></span></pre>
<h2><span style="font-weight: 400;">Son d&uuml;ş&uuml;nceler</span></h2>
<p><span style="font-weight: 400;">Bu componenti Material UI k&uuml;t&uuml;phanesinden algoritma ve g&ouml;r&uuml;n&uuml;ş&uuml;n&uuml; esinlenerek geliştirdim. Kodların en doğru patternda yazıldığını da d&uuml;ş&uuml;nm&uuml;yorum.&nbsp;</span><span style="font-weight: 400;">Şurası şu şekilde değiştirilirse daha doğru olur diyenler olursa gerekli g&uuml;ncellemeleri yapmak isterim.</span></p>
<p><span style="font-weight: 400;">Okuduğunuz i&ccedil;in teşekk&uuml;r ederim, umarım faydalı olmuştur.</span></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[GORM Preload Obje Limitleme (Limit Preloaded Object Count)]]></title>
            <link>https://dogukan.dev/gorm-preload-obje-limitleme-limit-preloaded-object-count</link>
            <guid>https://dogukan.dev/gorm-preload-obje-limitleme-limit-preloaded-object-count</guid>
            <pubDate>Sat, 24 Sep 2022 01:25:30 GMT</pubDate>
            <description><![CDATA[Bildiğimiz üzere GORM, Golang üzerinde kullandığımız object relationship managerlardan birisi. Kendisi çoğu zaman zor işleri kolay şekilde çözse de bazı işlerin üstesinden gelmeyi daha da zorlaştırabiliyor :)
Karşılaştığım ve...]]></description>
            <content:encoded><![CDATA[<p>Bildiğimiz &uuml;zere GORM, Golang &uuml;zerinde kullandığımız object relationship managerlardan birisi. Kendisi &ccedil;oğu zaman zor işleri kolay şekilde &ccedil;&ouml;zse de bazı işlerin &uuml;stesinden gelmeyi daha da zorlaştırabiliyor :)</p>
<p>Karşılaştığım ve &ccedil;&ouml;z&uuml;m getireceğimiz sorun ise bir nesnenin i&ccedil;indeki ilişkisel verilerin kısıtlı sayıda getirilmesi problemidir. Daha detaylı olarak şu şekilde bahsedebiliriz.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/dia1.jpg" alt="Example diagram" width="441" height="121" /></p>
<p><span style="font-weight: 400;">Yukarıdaki yapıda bir tablomuz olduğunu ve ikisi arasında one to many ilişkisi olduğunu varsayalım. GORM &uuml;zerinde şu şekilde bir d&ouml;n&uuml;ş beklediğimizde preload işlemi yapıyoruz. </span></p>
<pre>[<br />&nbsp; {<br />&nbsp; &nbsp; "id": 1,<br />&nbsp; &nbsp; "content": "1. i&ccedil;erik",<br />&nbsp; &nbsp; "comments": [<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 1,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "1. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 2,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "2. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 3,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "3. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 4,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "4. yorum"<br />&nbsp; &nbsp; &nbsp; }<br />  &nbsp;   // belki 1000 yorumdan fazla var?<br />&nbsp; &nbsp; ]<br />&nbsp; },<br />&nbsp; {<br />&nbsp; &nbsp; "id": 2,<br />&nbsp; &nbsp; "content": "2. i&ccedil;erik",<br />&nbsp; &nbsp; "comments": [<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 5,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "1. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 6,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "2. yorum"<br />&nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; ]<br />&nbsp; }<br />]</pre>
<p><span style="font-weight: 400;">Preload kullandığımızda g&ouml;rd&uuml;ğ&uuml;n&uuml;z gibi iki i&ccedil;eriğin de yorumları başarıyla y&uuml;klendi. Ancak bu i&ccedil;eriklerin 500+ yorumu olduğunda <strong>preload</strong> yaptığımızda ne yapacağız? </span></p>
<p><span style="font-weight: 400;">İnternette &ouml;nerilen kılavuz şu şekilde: </span></p>
<pre><span style="font-weight: 400;">posts := []*models.Post{}<br />database.Connection().Model(&amp;posts).Preload(&ldquo;Comments&rdquo;, func(tx *gorm.DB) *gorm.DB {<br />&nbsp; &nbsp; return tx.Limit(2)<br />})<br /></span></pre>
<p><span style="font-weight: 400;">G&ouml;r&uuml;n&uuml;ş olarak bir sıkıntı g&ouml;z&uuml;kmese de bu bizim beklediğimiz &ccedil;alışma bi&ccedil;imine malesef sahip değil. Yukarıdaki kod bloğu tx.Limit(2) dediğinizde her Post nesnesi i&ccedil;in 2 adet değil o anda &ccedil;ekilen t&uuml;m Post nesneleri i&ccedil;in toplam 2 adet Comment d&ouml;nd&uuml;recektir. &Ccedil;ıktı olarak alacağınız sonu&ccedil; bu şekildedir. </span></p>
<pre><span style="font-weight: 400;">[<br />&nbsp; {<br />&nbsp; &nbsp; "id": 1,<br />&nbsp; &nbsp; "content": "1. i&ccedil;erik",<br />&nbsp; &nbsp; "comments": [<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 1,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "1. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 2,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "2. yorum"<br />&nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; ]<br />&nbsp; },<br />&nbsp; {<br />&nbsp; &nbsp; "id": 2,<br />&nbsp; &nbsp; "content": "2. i&ccedil;erik",<br />&nbsp; &nbsp; "comments": [<br />&nbsp; &nbsp; &nbsp; &nbsp; // aslında devamı var ancak &ccedil;ekmiyor :)<br />  &nbsp;     // &ccedil;&uuml;nk&uuml; toplamda 2 adet nesne &ccedil;ekti ve query&rsquo;i sonlandırdı<br />&nbsp; &nbsp; ]<br />&nbsp; }<br />]<br /></span></pre>
<p><span style="font-weight: 400;">&Ccedil;&ouml;z&uuml;m&uuml; i&ccedil;in ise yukarıdaki kod bloğuna daha komplike bir SQL sorgusu yazmamız gerekecek. Burada imdadımıza lateral join ve distinct selection yetişiyor.&nbsp; </span></p>
<pre><span style="font-weight: 400;">posts := []*models.Post{}<br />database.Connection().Model(&amp;posts).Preload("Comments", func(tx *gorm.DB) *gorm.DB {<br />&nbsp; &nbsp; return tx.Joins(`<br />  &nbsp;     JOIN LATERAL (<br />  &nbsp; &nbsp; &nbsp;     SELECT DISTINCT c.post_id FROM comments c WHERE c.post_id = comments.post_id LIMIT 2<br />  &nbsp;     ) AS cm ON cm.post_id = comments.post_id<br />    `)<br />})<br /></span></pre>
<p><span style="font-weight: 400;">Yukarıdaki şekilde join işlemi yaptığımızda beklediğimiz &ccedil;ıktıyı alacağız. </span></p>
<p><span style="font-weight: 400;">&Ouml;rnek &ccedil;ıktı:</span></p>
<pre><span style="font-weight: 400;">[<br />&nbsp; {<br />&nbsp; &nbsp; "id": 1,<br />&nbsp; &nbsp; "content": "1. i&ccedil;erik",<br />&nbsp; &nbsp; "comments": [<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 1,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "1. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 2,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "2. yorum"<br />&nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; ]<br />&nbsp; },<br />&nbsp; {<br />&nbsp; &nbsp; "id": 2,<br />&nbsp; &nbsp; "content": "2. i&ccedil;erik",<br />&nbsp; &nbsp; "comments": [<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 5,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "1. yorum"<br />&nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; "id": 6,<br />&nbsp; &nbsp; &nbsp; &nbsp; "content": "2. yorum"<br />&nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; ]<br />&nbsp; }<br />]<br /></span></pre>
<p><span style="font-weight: 400;">Okuduğunuz i&ccedil;in teşekk&uuml;r ederim, umarım faydalı olmuştur :) Sağlıcakla kalın. </span></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Golang Fiber ile Integration Test Geliştirmek]]></title>
            <link>https://dogukan.dev/golang-fiber-ile-integration-test-gelistirmek</link>
            <guid>https://dogukan.dev/golang-fiber-ile-integration-test-gelistirmek</guid>
            <pubDate>Tue, 12 Apr 2022 12:30:41 GMT</pubDate>
            <description><![CDATA[
Bu makalemizde Golang üzerindeki Gofiber frameworkü üzerinde nasıl integration test geliştirebiliriz bu konu üzerine konuşacağız. Integration test nedir buna değinerek başlayabiliriz.
Integration test nedir?
Integration t...]]></description>
            <content:encoded><![CDATA[<p><span style="font-weight: 400;"><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/golang-integration-test.jpg" alt="Golang Integration Test" width="1500" height="864" /></span></p>
<p><span style="font-weight: 400;">Bu makalemizde <strong>Golang</strong> &uuml;zerindeki <strong>Gofiber framework</strong>&uuml; &uuml;zerinde nasıl&nbsp;</span><span style="font-weight: 400;"><strong>integration test</strong> geliştirebiliriz bu konu &uuml;zerine konuşacağız. <strong>Integration test</strong> nedir buna değinerek başlayabiliriz.</span></p>
<h2><span style="font-weight: 400;">Integration test nedir?</span></h2>
<p><span style="font-weight: 400;"><strong>Integration test</strong> unitlerin birleştiğinde istenilen davranışı sergilemesini kontrol ettiğimiz testler b&uuml;t&uuml;n&uuml;d&uuml;r. &Ouml;rnek olarak bahsedersek uygulamamızda A, B ve C işlemleri i&ccedil;in unit testler geliştirdik ve &uuml;&ccedil; işlem de testlere uygun &ccedil;ıktılar sağladı. Ardından bu &uuml;&ccedil; işlemi kullanarak farklı işlemler ger&ccedil;ekleştiren yeni bir fonksiyon oluşturduk. Bu fonksiyonun test edilmesine integration test diyebiliriz.&nbsp;<br /></span><span style="font-weight: 400;">Aynı zamanda <strong>HTTP test</strong>leri de integration testlerine girmektedir. Bug&uuml;n vereceğimiz &ouml;rneklerimizde de <strong>Fiber Handler</strong> yani controllerlarımız &uuml;zerinde test ger&ccedil;ekleştireceğiz.</span></p>
<h2><span style="font-weight: 400;">Integration test &ouml;nemi nedir?</span></h2>
<p><span style="font-weight: 400;"><strong>Integration test</strong>leri uygulamamızda y&uuml;ksek &ouml;nem taşımaktadır. Testlerin yazıldığı fonksiyonlar &uuml;zerinde değişiklikler ger&ccedil;ekleştiğinde uygulama halen belirtilen &ccedil;ıktıları bize sağlıyor mu, d&uuml;zg&uuml;n şekilde işleyişi mevcut mu bunları g&ouml;z ile g&ouml;rmektense otomatik şekilde tespitini sağlamaktadır. Anlaşılması i&ccedil;in bir &ouml;rnek sunalım.</span></p>
<p><span style="font-weight: 400;">Sisteminizde login olmanızı sağlayan ve <strong>JWT token</strong> &uuml;reten bir kontrolc&uuml; mevcut olduğunu varsayalım. Bu kontrolc&uuml;n&uuml;n doğru şekilde &ccedil;alıştığından emin olmamız i&ccedil;in &ouml;nce bir <strong>dummy user</strong> oluşturmamız gerekir. İlk aşamadaki testimiz bizim kullanıcımızın oluşup oluşmadığını kontrol etmektedir. Eğer ilk testte belirtilen &ccedil;ıktı ger&ccedil;ekleştiyse login işlemini ger&ccedil;ekleştirebiliriz. İkinci test koşulu olarak da oluşturulmuş kullanıcının ger&ccedil;ekten giriş yaptığında <strong>JWT token</strong> elde edebildiğini kontrol etmemiz gerekir. Bu iki koşul sağlandığında sisteminizdeki kullanıcılar ger&ccedil;ek ortamda da sorunsuz şekilde giriş yapabildiğini varsayabiliriz. &Ouml;nemi ise bu login sistemi &uuml;zerinde iyileştirmeler ve yeni eklemeler yaptığınızda hata &ccedil;ıkma olasılığı her an mevcuttur ancak yazdığımız bu testler sayesinde tek tuş ile kullanıcılar halen başarılı şekilde akışa uygun işlem ger&ccedil;ekleştirebiliyor mu test edebiliriz.</span></p>
<h2><span style="font-weight: 400;">Test koşullarını nasıl hazırlarım?</span></h2>
<p><span style="font-weight: 400;">Buradaki &ouml;nemli kısım <strong>platform bağımlı d&uuml;ş&uuml;nmek değil</strong>, bu uygulamanın otomatize testlerinde ne gibi koşullarla karşılaşılmasının <strong>beklendiğidir</strong>. &Ouml;rneğin bir modelinizde CRUD işlemleri ger&ccedil;ekleştiren handlerlarınız olduğunu d&uuml;ş&uuml;n&uuml;n. Create işleminde test edeceğimiz şey <strong>validasyon</strong>ların ger&ccedil;ekten &ccedil;alıştığının kontrol&uuml; </span><em><span style="font-weight: 400;">(boş alanlar, unique alan ihlali&hellip;)</span></em><span style="font-weight: 400;">, doğru girdiler verildiğinde ger&ccedil;ekten istenilen &ccedil;ıktı elde edilir mi bunların kontrol&uuml; yapılır. Delete işleminde hi&ccedil; varolmayan bir girdi verildiğinde <strong>HTTP response kodu</strong> ne d&ouml;ner, hata mesajı ger&ccedil;ekten handle edilmiş midir gibi koşulları kontrol edersiniz. Bu noktada oluşturacağınız testler tamamen hayal g&uuml;c&uuml;n&uuml;ze kalmış ancak yukarıdakiler temel olarak bulundurabileceğiniz &ouml;rneklerdir.</span></p>
<h2><span style="font-weight: 400;">Gofiber ile Integration Test nasıl geliştirilir?</span></h2>
<p><span style="font-weight: 400;"><strong>Gofiber</strong> &uuml;zerinde integration testleri geliştirmek i&ccedil;in klasik <strong>Golang test şablonu</strong>ndan dışarı &ccedil;ıkmayacağız. Ben dış bir k&uuml;t&uuml;phane eklemedim </span><em><span style="font-weight: 400;">(testify gibi) </span></em><span style="font-weight: 400;">ancak bunun tercihi de size kalmış. &Ouml;nemli olan temel mantığı kavramak.</span></p>
<p><span style="font-weight: 400;">Ben &ouml;rnek testimizi daha &ouml;nce paylaştığım <code>golang-rest-api-boilerplate</code> &uuml;zerinden anlatacağım. O &ouml;rnekte tanımladığımız Post modeli &uuml;zerindeki Index ve Create handlerlarına test geliştirdim. Burada bir Create handlerine nasıl test yazmışım bunu inceleyelim.</span></p>
<p><span style="font-weight: 400;">İlk &ouml;nce <code>create_test.go</code> dosyamı <code>create.go</code> dosyasının bulunduğu dizinde oluşturuyorum. Ardından dosyanın i&ccedil;eriğini aşağıdaki şekilde tanımlıyorum. Tanımlamanın ardından gerekli kod blokları &uuml;zerine konuşacağım.</span></p>
<pre><span style="font-weight: 400;">package post<br />&nbsp;<br />import (<br />&nbsp; &nbsp; "bytes"<br />&nbsp; &nbsp; "encoding/json"<br />&nbsp; &nbsp; "io"<br />&nbsp; &nbsp; "net/http/httptest"<br />&nbsp; &nbsp; "testing"<br />&nbsp;<br />&nbsp; &nbsp; "github.com/gofiber/fiber/v2"<br />)<br />&nbsp;<br />func TestCreate(t *testing.T) {<br />&nbsp; &nbsp; type wanted struct {<br />&nbsp; &nbsp; &nbsp; &nbsp; statusCode &nbsp; int<br />&nbsp; &nbsp; &nbsp; &nbsp; expectedKeys []string<br />&nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; // Create fiber app for testing purposes<br />&nbsp; &nbsp; app := fiber.New()<br />&nbsp; &nbsp; app.Post("/", Create)<br />&nbsp;<br />&nbsp; &nbsp; // Tests struct<br />&nbsp; &nbsp; tests := []struct {<br />&nbsp; &nbsp; &nbsp; &nbsp; name &nbsp; &nbsp; &nbsp; &nbsp;string<br />&nbsp; &nbsp; &nbsp; &nbsp; description string<br />&nbsp; &nbsp; &nbsp; &nbsp; endpoint &nbsp; &nbsp;string<br />&nbsp; &nbsp; &nbsp; &nbsp; payload &nbsp; &nbsp; any<br />&nbsp; &nbsp; &nbsp; &nbsp; want &nbsp; &nbsp; &nbsp; &nbsp;wanted<br />&nbsp; &nbsp; }{<br />&nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: &nbsp; &nbsp; &nbsp; &nbsp;"Create a fake post",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; description: "This test should return 200 status code and created post",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endpoint: &nbsp; &nbsp;"/",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; payload: map[string]any{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "title": &nbsp; "Example post",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "content": "Lorem ipsum",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; want: wanted{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; statusCode: 200,<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expectedKeys: []string{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "id",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "title",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "content",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: &nbsp; &nbsp; &nbsp; &nbsp;"Create a post with blank content",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; description: "This test should return 400 status code and error message",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endpoint: &nbsp; &nbsp;"/",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; payload: map[string]any{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "title": &nbsp; "Example post2",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "content": "",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; want: wanted{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; statusCode: 400,<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expectedKeys: []string{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "message",<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; &nbsp; &nbsp; },<br />&nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; for _, tt := range tests {<br />&nbsp; &nbsp; &nbsp; &nbsp; t.Run(tt.name, func(t *testing.T) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body, err := json.Marshal(tt.payload)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("Cannot parse body: %v", err)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Fail()<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; req := httptest.NewRequest("POST", tt.endpoint, bytes.NewReader(body))<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; req.Header.Add("Content-Type", "application/json")<br />&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Create request<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; res, err := app.Test(req)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("Cannot test Fiber handler: %v", err)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Fail()<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Assertions<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if res.StatusCode != tt.want.statusCode {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("Expected status code %d, got %d", tt.want.statusCode, res.StatusCode)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; answer, err := io.ReadAll(res.Body)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("Cannot parse body: %v", err)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var message map[string]any<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err = json.Unmarshal([]byte(answer), &amp;message)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("Cannot unmarshal response: %v", err)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for _, s := range tt.want.expectedKeys {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if _, ok := message[s]; !ok {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.Errorf("Expected response body to contain key %s but it can not be found", s)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; })<br />&nbsp; &nbsp; }<br />}<br /></span></pre>
<p><span style="font-weight: 400;">İlk aşamada <code>TestCreate</code> isimli fonksiyonumuzu oluşturuyor ve inbuilt <strong>test k&uuml;t&uuml;phanesini</strong> parametre olarak fonksiyona ekliyoruz.</span></p>
<p><span style="font-weight: 400;">Ardından testin &ccedil;ıktısından tam olarak ne beklediğime dair verileri ekleyeceğim <code>wanted</code> isimli bir struct tanımlıyorum. Ben d&ouml;nen <strong>status code</strong>ları ve bodyden gelen <strong>JSON key</strong>lerini kontrol etmek istiyorum. Bu alanları structumda oluşturuyorum.</span></p>
<p><span style="font-weight: 400;">Bir <strong>gofiber</strong> uygulamasını test etmek i&ccedil;in <code>fiber.New()</code> diyerek fiber uygulamamı oluşturuyorum. Ardından test edeceğim handlerin <strong>rota</strong>sını ekliyorum. Siz isterseniz birden &ccedil;ok rota ekleyerek zincirleme testler de geliştirebilirsiniz. Biz bu &ouml;rnekte tek bir rota &uuml;zerinden ilerleyeceğiz.</span></p>
<p><span style="font-weight: 400;">Ardından <code>tests</code> structumu oluşturuyorum. Burada olmazsa olmazlarımız <code>name</code>, <code>description</code> ve <code>want</code> fieldlarıdır. Ben daha dinamik olması adına <code>endpoint</code> ve <code>payload</code> isimli iki field daha ekliyorum.&nbsp;</span></p>
<p><span style="font-weight: 400;">Sonraki aşamada bu structun i&ccedil;eriğini oluşturmamız gerekiyor. Bu aşamada iki adet test oluşturuyorum. Birinde başarılı şekilde g&ouml;nderimin oluşturulup, 200 <strong>status code</strong> d&ouml;nd&uuml;rmesini ve JSON&rsquo;u <strong>unmarshal</strong> ettiğimde istediğim keylerin bulunup bulunmadığını test ediyorum. Normal şartlarda g&ouml;nderimin oluşturulup <code>id</code>, <code>title</code> ve <code>content</code> d&ouml;nd&uuml;rmesi gerektiğini biliyorum.</span></p>
<p><span style="font-weight: 400;">İkinci testimde ise uygulamam <code>content</code> kısmının boş olmasını kabul etmeyip <strong>400</strong> d&ouml;nd&uuml;r&uuml;yor. Ben de bunun ger&ccedil;ekten istenilen şekilde &ccedil;alışıp &ccedil;alışmadığını kontrol etmek i&ccedil;in boş contente sahip bir g&ouml;nderi oluşturtuyorum. Ardından <strong>400 status code</strong> d&ouml;nmesini ve d&ouml;nen i&ccedil;erikte de sadece <code>message</code> alanının olmasını bekliyorum.</span></p>
<p><span style="font-weight: 400;">Testlerimi tanımladığıma g&ouml;re artık ger&ccedil;ek kontrolleri yapmaya başlayabiliriz. <strong>for</strong> d&ouml;ng&uuml;s&uuml; ile testlerimi &ccedil;alıştırmaya başlıyorum. <strong>Test kontrol koşulları</strong>mı <code>t.Run()</code> fonksiyonu i&ccedil;erisinde yazacağım.&nbsp;<br /></span><span style="font-weight: 400;">İlk aşamada test payloadımın <strong>marshal</strong> edilip edilemediğini kontrol ediyorum. Bu aşama ger&ccedil;ekleşmezse diğer aşamalarda ilerlenmesinin bir anlamı olmadığından <code>t.Fail()</code> diyerek testi anında sonlandırıyorum.</span></p>
<p><span style="font-weight: 400;">Ardından handlerımı test etmek i&ccedil;in bir <strong>HTTP request</strong>i g&ouml;ndermem lazım. Bunu da inbuilt gelen <strong>httptest</strong> k&uuml;t&uuml;phanesi ile ger&ccedil;ekleştiriyorum. Yeni bir post requesti oluşturup endpointi ve payloadımı ekliyorum. Payloadımın <code>application/json</code> olduğunu belirtmem fiber i&ccedil;in &ccedil;ok &ouml;nemli. Aksi takdirde <strong>bodyParser</strong> i&ccedil;eriğimizi kullanamayacaktır.</span></p>
<p><span style="font-weight: 400;"><strong>Request</strong>i oluşturduktan sonra <code>app.Test(req)</code> diyerek fiber i&ccedil;erisinde gelen test fonksiyonu ile &ccedil;alıştırıyorum. &Ccedil;alıştırdıktan sonra ilk aşamada beklediğim <strong>status code</strong> gelmiş mi diye kontrol ediyorum.<br /></span><span style="font-weight: 400;">Bu aşamadan sonra da gelen i&ccedil;eriği parse edip gerekli <strong>keyler</strong> var mı diye kontrol ettikten sonra testimi sonlandırıyorum.</span></p>
<p><span style="font-weight: 400;"><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/Screenshot 2022-04-12 122412.jpg" alt="Golang Test" width="570" height="450" /></span></p>
<p><span style="font-weight: 400;"><strong>VSCode</strong> &uuml;zerinde <strong>Go eklentisi</strong> kurulu olduğunda testleri bu kısımdan kolayca g&ouml;r&uuml;nt&uuml;leyip &ccedil;alıştırabiliyoruz.</span></p>
<p><span style="font-weight: 400;">Yazdığım testlerin bulunduğu kodlara şu repodan ulaşabilirsiniz: </span><a href="https://github.com/dogukanoksuz/go-rest-api-boilerplate/tree/main/app/controllers/post" target="_blank" rel="noopener"><span style="font-weight: 400;">go-rest-api-boilerplate/app/controllers/post at main &middot; dogukanoksuz/go-rest-api-boilerplate (github.com)</span></a></p>
<p><span style="font-weight: 400;">Okuduğunuz i&ccedil;in teşekk&uuml;r ederim. Sorularınız ve geri bildirimleriniz i&ccedil;in aşağıdaki yorum b&ouml;l&uuml;m&uuml;n&uuml; kullanabilirsiniz. İyi kodlamalar &lt;3</span></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Golang REST API ve CRUD İşlemleri]]></title>
            <link>https://dogukan.dev/golang-rest-api-crud-islemleri</link>
            <guid>https://dogukan.dev/golang-rest-api-crud-islemleri</guid>
            <pubDate>Sat, 12 Mar 2022 20:55:32 GMT</pubDate>
            <description><![CDATA[
Bugün Golang ile Gofiber kullanarak yüksek performanslı bir REST API nasıl geliştirilir bunun üzerine konuşacağız. 
Gofiber framework kullanmamızın sebebini şu şekilde listeleyebiliriz:

Yüksek performans ve düşük bellek...]]></description>
            <content:encoded><![CDATA[<p><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/go-programlama-dili-gopher.png" alt="Golang" width="881" height="441" /></p>
<p><span style="font-weight: 400;">Bug&uuml;n <strong>Golang </strong>ile <strong>Gofiber </strong>kullanarak <strong>y&uuml;ksek performanslı</strong> bir <strong>REST API</strong> nasıl geliştirilir bunun &uuml;zerine konuşacağız.&nbsp;</span></p>
<p><span style="font-weight: 400;"><strong>Gofiber framework</strong> kullanmamızın sebebini şu şekilde listeleyebiliriz:</span></p>
<ul>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Y&uuml;ksek performans ve d&uuml;ş&uuml;k bellek kullanımı</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Hızlı server-side programlama</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">K&uuml;t&uuml;phane tarafından sağlanan middlewarelar</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Kolay rotalama</span></li>
</ul>
<p>&nbsp;</p>
<h3><span style="font-weight: 400;">Proje Oluşturma</span></h3>
<p><span style="font-weight: 400;">Genel olarak konuya giriş yaptığımıza g&ouml;re Go projemizi oluşturarak devam edebiliriz. Boş bir Go projesi oluşturmak i&ccedil;in şu işlemleri yapalım.</span></p>
<pre><span style="font-weight: 400;">$ mkdir restapi</span><br /><br /><span style="font-weight: 400;">$ go mod init github.com/USERNAME/REPO_NAME</span></pre>
<p><span style="font-weight: 400;">Bu işlemleri yaptıktan sonra gerekli klas&ouml;rler ve dosyalar oluşacaktır. Şimdi proje dizinimizde main.go dosyamızı oluşturalım, ardından gofiber k&uuml;t&uuml;phanesini projemize aşağıdaki komut ile ekleyelim.</span></p>
<pre><span style="font-weight: 400;">$ go get github.com/gofiber/fiber/v2</span></pre>
<h3>&nbsp;</h3>
<h3><span style="font-weight: 400;">Klas&ouml;r Yapısı</span></h3>
<h4><span style="font-weight: 400;">/app</span></h4>
<p><span style="font-weight: 400;">Bu klas&ouml;r bizim genel Fiber uygulamamıza dair fonksiyonları barındırmalıdır. Şu şekilde kapsamlandırabiliriz.</span></p>
<ul>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">controllers</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Rotalarda kullanılacak controllerları burada tanımlarız.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">models</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Uygulamamızdaki modelleri bu klas&ouml;r altında oluşturmalıyız.</span><span style="font-weight: 400;"><br /></span></li>
</ul>
<h4><span style="font-weight: 400;">/pkg</span></h4>
<ul>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">configs</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Bu klas&ouml;rde uygulamamıza ait konfig&uuml;rasyon dosyalarını tutabiliriz.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">middleware</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Bu klas&ouml;rde fiber uygulamamız i&ccedil;in gerekli middlewareları oluşturabiliriz.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">routes</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Bu klas&ouml;r i&ccedil;erisinde projemize ait rotaları tutabiliriz.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">utils</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Bu klas&ouml;rde sunucumuzu başlatan, hataları kontrol eden veya uygun g&ouml;rd&uuml;ğ&uuml;m&uuml;z dosyaları oluşturabiliriz.</span></li>
</ul>
<p>&nbsp;</p>
<h4><span style="font-weight: 400;">/platform</span></h4>
<ul>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">cache</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Bu klas&ouml;rde kullanacağımız &ouml;nbellekleme &ccedil;&ouml;z&uuml;m&uuml;n&uuml; implemente edebiliriz.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">database</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Veritabanı bağlantılarını ve diğer işlemleri yapacağımız kısım.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">migrations</span><span style="font-weight: 400;"><br /></span><span style="font-weight: 400;">Migrationları oluşturacağımız klas&ouml;r</span><span style="font-weight: 400;"><br /><br /></span></li>
</ul>
<h3><span style="font-weight: 400;">Fiber Sunucusu</span></h3>
<p><span style="font-weight: 400;">Bunun i&ccedil;in &ouml;ncelikle <code>pkg/utils</code> altında <code>server.go</code> dosyamızı oluşturalım ve i&ccedil;eriğini aşağıdaki şekilde ekleyelim.</span></p>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> utils</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"fmt"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"log"</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/gofiber/fiber/v2"</span><br /><span style="font-weight: 400;">)</span><br /><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">CreateServer</span><span style="font-weight: 400;">(port </span><span style="font-weight: 400;">int</span><span style="font-weight: 400;">) {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">// Create Fiber App</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;app := fiber.</span><span style="font-weight: 400;">New</span><span style="font-weight: 400;">()</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">// Start server</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;log.</span><span style="font-weight: 400;">Fatal</span><span style="font-weight: 400;">(app.</span><span style="font-weight: 400;">Listen</span><span style="font-weight: 400;">(fmt.</span><span style="font-weight: 400;">Sprintf</span><span style="font-weight: 400;">(</span><span style="font-weight: 400;">":</span><span style="font-weight: 400;">%d</span><span style="font-weight: 400;">"</span><span style="font-weight: 400;">, port)))</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">Ardından bu fonksiyona bir port değişkeni vererek <code>main.go</code> i&ccedil;erisinde şu şekilde &ccedil;ağırabiliriz.</span></p>
<p><span style="font-weight: 400;">Bu fonksiyon i&ccedil;erisinde <strong>middleware</strong>larımızı ve rotalarımızı kaydedeceğiz.</span></p>
<p>&nbsp;</p>
<p><span style="font-weight: 400;">&Ouml;rnek <code>main.go</code> i&ccedil;eriği:</span></p>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> main</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/pkg/utils"</span><br /><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">main</span><span style="font-weight: 400;">() {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;utils.</span><span style="font-weight: 400;">CreateServer</span><span style="font-weight: 400;">(3000)</span><br /><span style="font-weight: 400;">}</span></pre>
<h3>&nbsp;</h3>
<h3><span style="font-weight: 400;">Model Oluşturmak</span></h3>
<p><span style="font-weight: 400;">Genel olarak projemizin klas&ouml;r yapısından bahsettiğimize g&ouml;re bu tarz bir yapıyı oluşturalım.&nbsp;</span></p>
<p><span style="font-weight: 400;">Ardından <code>models</code> klas&ouml;r&uuml;mizin i&ccedil;erisinde ilk modelimizi oluşturalım. Ben post yani g&ouml;nderi isminde bir model oluşturacağım. Dosya i&ccedil;eriğini aşağıdaki gibi tanımlayabiliriz.</span></p>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> models</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/google/uuid"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"gorm.io/gorm"</span><br /><span style="font-weight: 400;">)</span><br /><br /><span style="font-weight: 400;">type</span><span style="font-weight: 400;"> Post </span><span style="font-weight: 400;">struct</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;ID&nbsp; &nbsp; &nbsp; </span><span style="font-weight: 400;">string</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">`gorm:"primary_key"`</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;Title &nbsp; </span><span style="font-weight: 400;">string</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;Content </span><span style="font-weight: 400;">string</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">`gorm:"type:text"`</span><br /><span style="font-weight: 400;">}</span><br /><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> (post *Post) </span><span style="font-weight: 400;">BeforeCreate</span><span style="font-weight: 400;">(tx *gorm.DB) </span><span style="font-weight: 400;">error</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;post.ID = uuid.</span><span style="font-weight: 400;">NewString</span><span style="font-weight: 400;">()</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">nil</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">Oluşturduktan sonra ilk modelimiz hazır. Genel olarak detaylarından bahsetmek gerekirse ID alanı i&ccedil;in UUID oluşturuyoruz. Bunun i&ccedil;in de <strong>GORM</strong>&rsquo;un <strong>pre-hook</strong>larından faydalanarak bir <strong>UUID</strong> tanımlıyoruz.&nbsp;</span></p>
<h3>&nbsp;</h3>
<h3><span style="font-weight: 400;">Veritabanı Bağlantısı</span></h3>
<p><span style="font-weight: 400;">&Ouml;ncelikle kendi ortamınızda bir <strong>veritabanı sunucusu</strong>na sahip olmanız gerekmektedir. Ben <a title="Devilbox ile LAMP Stack Kurulumu" href="/devilbox-ile-lamp-stack-kurulumu.html" target="_blank" rel="noopener">devilbox</a> kullanarak bir <strong>MariaDB</strong> instanceı oluşturdum. Siz de benzer programlarla veritabanınızı oluşturun.</span></p>
<p><span style="font-weight: 400;">Bu işlemin ardından <code>platform/database</code> klas&ouml;r&uuml; altında <code>mysql.go</code> isminde bir dosya oluşturun.</span></p>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> database</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"fmt"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"os"</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"gorm.io/driver/mysql"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"gorm.io/gorm"</span><br /><span style="font-weight: 400;">)</span><br /><br /><span style="font-weight: 400;">var</span><span style="font-weight: 400;"> Conn *gorm.DB</span><br /><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">Init</span><span style="font-weight: 400;">() </span><span style="font-weight: 400;">error</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">var</span><span style="font-weight: 400;"> err </span><span style="font-weight: 400;">error</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;dsn := fmt.</span><span style="font-weight: 400;">Sprintf</span><span style="font-weight: 400;">(</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"</span><span style="font-weight: 400;">%s</span><span style="font-weight: 400;">:</span><span style="font-weight: 400;">%s</span><span style="font-weight: 400;">@tcp(</span><span style="font-weight: 400;">%s</span><span style="font-weight: 400;">:</span><span style="font-weight: 400;">%s</span><span style="font-weight: 400;">)/</span><span style="font-weight: 400;">%s</span><span style="font-weight: 400;">?charset=utf8mb4&amp;parseTime=True&amp;loc=Local"</span><span style="font-weight: 400;">,</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"DB_USER"</span><span style="font-weight: 400;">,</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"DB_PASS"</span><span style="font-weight: 400;">,</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"DB_HOST"</span><span style="font-weight: 400;">,</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"DB_PORT"</span><span style="font-weight: 400;">,</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"DB_NAME"</span><span style="font-weight: 400;">,</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;)</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;Conn, err = gorm.</span><span style="font-weight: 400;">Open</span><span style="font-weight: 400;">(mysql.</span><span style="font-weight: 400;">Open</span><span style="font-weight: 400;">(dsn), &amp;gorm.Config{})</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">if</span><span style="font-weight: 400;"> err != </span><span style="font-weight: 400;">nil</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> err</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;}</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">nil</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">Yukarıdaki şekilde tanımlama yaptıktan sonra <code>DB_USER</code> gibi boşlukları kendi veritabanı bilgileriniz ile değiştirin. Ardından <code>database.Init()</code> şeklinde <code>server.go</code> dosyanız i&ccedil;erisinde &ccedil;ağırımı yaparak sunucu başlamadan &ouml;nce veritabanı bağlantınızı a&ccedil;abilirsiniz.</span></p>
<h3>&nbsp;</h3>
<h3><span style="font-weight: 400;">Migrationlar</span></h3>
<p><span style="font-weight: 400;">Migration işlemi oluşturduğumuz modellerin otomatik şekilde veritabanında tablolarının eklenmesi işlemidir. Bunun i&ccedil;in <code>platform/migrations</code> altında <code>migrate.go</code> isminde bir dosya oluşturalım ve i&ccedil;eriğini aşağıdaki şekilde girelim.</span></p>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> migrations</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/app/models"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/platform/database"</span><br /><span style="font-weight: 400;">)</span><br /><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">Migrate</span><span style="font-weight: 400;">() {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;database.Conn.</span><span style="font-weight: 400;">AutoMigrate</span><span style="font-weight: 400;">(&amp;models.Post{})</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">Bu kısımdaki <code>AutoMigrate</code> fonksiyonu <strong>GORM</strong>&rsquo;un i&ccedil;erisinde gelen bir fonksiyondur. Diğer oluşturduğunuz modeller (varsa) bu kısımda tanımlayarak uygulama başlangıcında tablolarının oluşturulmasını sağlayabilirsiniz. Bu fonksiyonu <code>CreateServer</code> fonksiyonu altında <code>database.Init</code> fonksiyonundan hemen sonra &ccedil;ağırabiliriz.</span></p>
<h3>&nbsp;</h3>
<h3><span style="font-weight: 400;">Kontrolc&uuml; Oluşturmak</span></h3>
<p><span style="font-weight: 400;">Kontrolc&uuml;ler projemizin &ouml;nemli bir ayağını oluşturmaktadır. Fiber tarafında ismi <strong>Handler</strong> olarak ge&ccedil;mektedir ve rotalarımızın ucunu bağladığımız Go fonksiyonlarına kontrolc&uuml; demekteyiz. Kontrolc&uuml;ler bizim i&ccedil;in postlarımızı listeleme, oluşturma, g&uuml;ncelleme ve silme işlemlerini yapabilir.</span></p>
<p><span style="font-weight: 400;">GORM ve Fiber kullanarak listeleme ve oluşturma işlemlerini &ouml;rnek olarak inceleyelim.</span></p>
<h4><span style="font-weight: 400;">Oluşturma (Create)</span></h4>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> post</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/app/models"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/platform/database"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/gofiber/fiber/v2"</span><br /><span style="font-weight: 400;">)</span><br /><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">Create</span><span style="font-weight: 400;">(ctx *fiber.Ctx) </span><span style="font-weight: 400;">error</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;post := models.Post{}</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">if</span><span style="font-weight: 400;"> err := ctx.</span><span style="font-weight: 400;">BodyParser</span><span style="font-weight: 400;">(&amp;post); err != </span><span style="font-weight: 400;">nil</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> ctx.</span><span style="font-weight: 400;">Status</span><span style="font-weight: 400;">(</span><span style="font-weight: 400;">503</span><span style="font-weight: 400;">).</span><span style="font-weight: 400;">JSON</span><span style="font-weight: 400;">(err)</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;}</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">if</span><span style="font-weight: 400;"> err := database.Conn.</span><span style="font-weight: 400;">Create</span><span style="font-weight: 400;">(&amp;post).Error; err != </span><span style="font-weight: 400;">nil</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> ctx.</span><span style="font-weight: 400;">Status</span><span style="font-weight: 400;">(</span><span style="font-weight: 400;">503</span><span style="font-weight: 400;">).</span><span style="font-weight: 400;">JSON</span><span style="font-weight: 400;">(err)</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;}</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> ctx.</span><span style="font-weight: 400;">JSON</span><span style="font-weight: 400;">(post)</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">Bu dosyayı <code>app/controllers/post/create.go</code> olarak oluşturalım ve inceleyelim. &Ouml;ncelikle Fiber&rsquo;ın <code>BodyParser</code> fonksiyonunu kullanarak bodyden gelen JSON&rsquo;u parse edelim. Ardından <code>database.Conn.Create</code> diyerek girdimizi veritabanına ekleyelim. İşlemlerin sonunda da kullanıcıya JSON olarak oluşturulan g&ouml;nderimizi g&ouml;nderebiliriz.</span></p>
<h4><span style="font-weight: 400;">Listeleme (Index/List)</span></h4>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> post</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/app/models"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/platform/database"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/gofiber/fiber/v2"</span><br /><span style="font-weight: 400;">)<br /></span><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">Index</span><span style="font-weight: 400;">(ctx *fiber.Ctx) </span><span style="font-weight: 400;">error</span><span style="font-weight: 400;"> {</span><br /><span style="font-weight: 400;"> &nbsp;&nbsp;&nbsp;posts := []models.Post{}</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;database.Conn.</span><span style="font-weight: 400;">Find</span><span style="font-weight: 400;">(&amp;posts)</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">return</span><span style="font-weight: 400;"> ctx.</span><span style="font-weight: 400;">JSON</span><span style="font-weight: 400;">(posts)</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">GORM ile bir modelin girdilerini listelemek istediğimizde yukarıdaki yola başvurabiliriz. <code>Find</code> fonksiyonu bizim i&ccedil;in t&uuml;m postları getirecektir ve bunu da kullanıcılara JSON olarak d&ouml;nd&uuml;rebiliriz.</span></p>
<p>&nbsp;</p>
<h3><span style="font-weight: 400;">Rota Tanımlamak</span></h3>
<p><span style="font-weight: 400;">Rotalar <strong>REST</strong> sunucumuzda hangi u&ccedil;ların hangi fonksiyonları &ccedil;ağıracağını, alacağı parametreleri vb. tanımladığımız yerdir. Gelin &ouml;rnek olarak yukarıdaki iki fonksiyonun rotalarını tanımlayalım.</span></p>
<p><span style="font-weight: 400;">Bunun <code>pkg/routes</code> altında <code>post.go</code> dosyamızı oluşturabiliriz. Bu dosyanın i&ccedil;eriği aşağıdaki gibi olmalıdır.</span></p>
<pre><span style="font-weight: 400;">package</span><span style="font-weight: 400;"> routes</span><br /><br /><span style="font-weight: 400;">import</span><span style="font-weight: 400;"> (</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/dogukanoksuz/go-rest-api-example/app/controllers/post"</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">"github.com/gofiber/fiber/v2"</span><br /><span style="font-weight: 400;">)<br /></span><br /><span style="font-weight: 400;">func</span><span style="font-weight: 400;"> </span><span style="font-weight: 400;">PostRoutes</span><span style="font-weight: 400;">(app *fiber.App) {</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">// List All Posts</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;app.</span><span style="font-weight: 400;">Get</span><span style="font-weight: 400;">(</span><span style="font-weight: 400;">"/posts"</span><span style="font-weight: 400;">, post.Index)</span><br /><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-weight: 400;">// Create Post</span><br /><span style="font-weight: 400;">&nbsp;&nbsp;&nbsp;&nbsp;app.</span><span style="font-weight: 400;">Post</span><span style="font-weight: 400;">(</span><span style="font-weight: 400;">"/post"</span><span style="font-weight: 400;">, post.Create)</span><br /><span style="font-weight: 400;">}</span></pre>
<p><span style="font-weight: 400;">Bu <code>PostRoutes</code> fonksiyonumuzu da <code>CreateServer</code> altında fiber app oluşturduktan hemen sonra &ccedil;ağırabiliriz. <code>routes.PostRoutes(app)</code> şeklinde tanımlamayı yaptığımızda rotalarımız kullanıma hazır olacaktır.</span></p>
<h3>&nbsp;</h3>
<h3><span style="font-weight: 400;">Derleme ve Denemeler</span></h3>
<p><span style="font-weight: 400;">Projeyi &ccedil;alıştırmak i&ccedil;in <code>go run main.go</code> şeklinde başlatabilir ya da <code>go build</code> diyerek oluşan executable&rsquo;ı &ccedil;alıştırabiliriz.</span></p>
<p><span style="font-weight: 400;">Temel olarak oluşturduğunuz <strong>REST API</strong>&rsquo;ı derleyip denemeler yapın. Bunun i&ccedil;in <strong>Postman</strong> programını veya sevdiğiniz bir <strong>REST client</strong>i kullanabilirsiniz.</span></p>
<p><span style="font-weight: 400;">Rotalarınıza <code>127.0.0.1:3000/posts</code> şeklinde erişebilirsiniz. Eğer veritabanında girdi oluşturulmuş ise başarılı şekilde d&ouml;n&uuml;ş&uuml;m&uuml;z&uuml; alacağız.</span></p>
<p>&nbsp;</p>
<h3><span style="font-weight: 400;">Tamamlanmış Proje</span></h3>
<p><a title="Golang REST API Boilerplate with Gofiber" href="https://github.com/dogukanoksuz/go-rest-api-boilerplate" target="_blank" rel="noopener">dogukanoksuz/go-rest-api-boilerplate: Golang REST API Boilerplate with Gofiber (github.com)</a><span style="font-weight: 400;"> altından projenin tamamlanmış haline ulaşabilirsiniz. Post modeli i&ccedil;in t&uuml;m <strong>CRUD</strong> işlemlerinin tamamlandığı &ouml;rnek bu şekilde incelenebilir.</span></p>
<p><span style="font-weight: 400;">Okuduğunuz i&ccedil;in teşekk&uuml;r ederim. Problemler i&ccedil;in GitHub reposu i&ccedil;erisinde issue oluşturabilir veya aşağıdaki yorumlar b&ouml;l&uuml;m&uuml;n&uuml; kullanabilirsiniz.</span></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Golang ile CLI Uygulaması Geliştirmek]]></title>
            <link>https://dogukan.dev/golang-ile-cli-uygulamasi-gelistirmek</link>
            <guid>https://dogukan.dev/golang-ile-cli-uygulamasi-gelistirmek</guid>
            <pubDate>Tue, 21 Sep 2021 19:55:32 GMT</pubDate>
            <description><![CDATA[
Bugün Golang kullanarak bir CLI uygulaması (konsol) nasıl geliştirebileceğimizden bahsedeceğim. Bir konsol uygulaması ne diye soracak olursanız şunları örnek verebiliriz. Systemctl, docker... gibi bir çok örneği mevcuttur. ...]]></description>
            <content:encoded><![CDATA[<p><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/go-programlama-dili-gopher.png" alt="Golang" width="881" height="441" /></p>
<p>Bug&uuml;n Golang kullanarak bir CLI uygulaması (konsol) nasıl geliştirebileceğimizden bahsedeceğim. Bir konsol uygulaması ne diye soracak olursanız şunları &ouml;rnek verebiliriz. Systemctl, docker... gibi bir &ccedil;ok &ouml;rneği mevcuttur.&nbsp;</p>
<h2>Golang nedir?</h2>
<p>Golang, basit, g&uuml;venli ve verimli yazılımlar geliştirmemize olanak tanıyan a&ccedil;ık kaynaklı bir programlama dilidir. Google tarafından desteklenmektedir. Derlenebilir bir dil olduğundan y&uuml;ksek performans sunmaktadır. Paket y&ouml;netimi sistemi, hızlı derleme s&uuml;resi b&uuml;y&uuml;k artılarındandır. Derlenen programların &ccedil;alıştırılması i&ccedil;in bir VM'e ihtiya&ccedil; duyulmamaktadır. (Java'daki bytecode runner &ouml;rnek verilebilir)</p>
<h2>Yeni bir Golang projesi oluşturmak</h2>
<h3>Golang kurulumu</h3>
<p>&Ouml;ncelikle sistemimize Go kurulumu yapmamız gerekmektedir. Derleme işlemlerini kendi i&ccedil;indeki ara&ccedil;lar ile yapacağız. Arch Linux&rsquo;da <code>pacman -S go</code>, Pardus/Debian &uuml;zerinde ise <code>apt install golang</code> yazarak kurulum işlemlerini ger&ccedil;ekleştirebiliriz. Kurulduğundan emin olmak i&ccedil;in ise terminalimizde <code>go version</code> komutunu &ccedil;alıştırarak kurulumun ger&ccedil;ekleştiğinden emin olalım.</p>
<h3>Yeni proje oluşturmak</h3>
<p>Yeni proje oluşturmak i&ccedil;in ev dizinimizde <code>go</code> isimli bir klas&ouml;r oluşturuyoruz. Ardından i&ccedil;ine de <code>src</code> isimli bir klas&ouml;r oluşturuyoruz. Bu klas&ouml;r i&ccedil;erisinde projelerimizi oluşturacağız. İlk aşama olarak <code>src</code> klas&ouml;r&uuml; i&ccedil;erisinde GitHub hesap isminiz ile klas&ouml;r oluşturmalısınız. Bu senaryoda benim <strong>dogukanoksuz</strong> isimli bir klas&ouml;r oluşturmam gerekmekte. Bu işlemlerin sonunda artık klas&ouml;r&uuml;m&uuml;z i&ccedil;erisine istediğimiz isimle projelerimizi oluşturabiliriz. &Ouml;rnek olarak <strong>go-cli-application</strong> isminde bir klas&ouml;r a&ccedil;alım ve terminalde bu dizine ge&ccedil;iş yapalım.</p>
<pre>$ go mod init github.com/GITHUB_HESAP_ADINIZ/go-cli-application</pre>
<p>komutunu &ccedil;alıştıralım. Bu komut sayesinde projemizde Go paket y&ouml;neticisini kullanabileceğiz.</p>
<h3>Code eklentisi kurulumu</h3>
<p>Eklentiler sekmesinden Go diye arattığımızda bu eklentinin kurulumunu yapıp Quick Start kısmını takip etmeliyiz. İşlemin ardından Go ortamınız otomatik formatlama, hata g&ouml;sterme ve tamamlama gibi &ouml;zellikleri alacaktır. Tavsiye ettiğim geliştirme ortamı bu şekildedir.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/golang.jpg" alt="Golang VSCode Extension" width="1034" height="582" /></p>
<h2>CLI uygulamasının temellerini atmak</h2>
<p>&Ouml;ncelikle kodlarımızın başlangı&ccedil; noktası olan main dosyamızı oluşturmamız gerekmektedir. Klas&ouml;r&uuml;m&uuml;z i&ccedil;erisinde <code>main.go</code> dosyasını oluşturalım. Ardından i&ccedil;eriği aşağıdaki gibi olmalıdır.</p>
<pre>package&nbsp;main<br /><br />func main() {<br /> &nbsp;&nbsp;&nbsp;// Uygulamamızın başlangı&ccedil; noktası<br />}</pre>
<p>Şu adımda bir Go projesi oluşturmuş olduk. Eğer bir Hello world mesajı g&ouml;rmek isterseniz <code>fmt.Println("Hello world")</code> şeklinde &ccedil;ağrımızı yapabiliriz.</p>
<p>Uygulamamızı derlemek i&ccedil;in de <code>go run main.go</code> veya <code>go build</code> komutlarını &ccedil;alıştırabiliriz. Ardından klas&ouml;r&uuml;m&uuml;z i&ccedil;erisinde &ccedil;alıştırılabilir dosyamız oluşacaktır.</p>
<p><img style="display: block; margin-left: auto; margin-right: auto;" src="/images/1/test.jpg" alt="Golang Hello World" width="889" height="103" /></p>
<p>Bu aşamadan sonra bir CLI uygulaması geliştirmek i&ccedil;in t&uuml;m hazırlıklarımızı tamamladık. Şimdi işimizi kolaylaştıracak bir k&uuml;t&uuml;phane kurulumu yapalım.</p>
<p><a href="https://github.com/spf13/cobra" target="_blank" rel="noopener">spf13/cobra</a> adresinden dok&uuml;manını inceleyebileceğimiz Cobra k&uuml;t&uuml;phanesini şu komut ile kuracağız.&nbsp;</p>
<pre>$ go get -u github.com/spf13/cobra </pre>
<p>komutunu &ccedil;alıştırarak k&uuml;t&uuml;phanemizi projeye ekleyelim.</p>
<h2>Cobra k&uuml;t&uuml;phanesi kullanımı</h2>
<p>Arg&uuml;manların yakalanması, parse edilmesi işlemlerini bizim yerimize kolayca halletmesi i&ccedil;in Cobra k&uuml;t&uuml;phanesini kullanacağız.</p>
<pre>▾ go-cli-application/<br /> &nbsp;&nbsp; ▾ cmd/<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buraya.go<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; komutlarinizi.go<br />  &nbsp; &nbsp; &nbsp; ekleyin.go<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; root.go<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main.go</pre>
<p>Klas&ouml;r yapımız yukarıdaki şekilde olmalıdır. Komutlarımızı <code>cmd</code> klas&ouml;r&uuml; altında a&ccedil;acağız. <code>main.go</code> dosyasının i&ccedil;eriğini de aşağıdaki gibi değiştirelim.</p>
<pre>package main<br /><br />import (<br />  "github.com/GITHUB_KULLANICI_ADINIZ/go-cli-application/cmd"<br />)<br /><br />func main() {<br />  cmd.Execute()<br />}</pre>
<p>Cobra k&uuml;t&uuml;phanesi artık t&uuml;m arg&uuml;manları yakalayacaktır. Gelin birlikte k&ouml;k komutumuzu oluşturalım. <code>cmd</code> klas&ouml;r&uuml; i&ccedil;ine <code>root.go</code> dosyamızı oluşturalım.</p>
<pre>package&nbsp;cmd<br /><br />import (<br /> &nbsp;&nbsp;&nbsp;"fmt"<br />&nbsp;&nbsp;&nbsp;&nbsp;"os"<br /><br /> &nbsp;&nbsp;&nbsp;"github.com/spf13/cobra"<br />)<br /><br />var (<br /> &nbsp;&nbsp;&nbsp;rootCmd&nbsp;=&nbsp;&amp;cobra.Command{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Use:&nbsp;&nbsp;&nbsp;"go-cli-application",<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Short:&nbsp;"Example&nbsp;Go&nbsp;CLI&nbsp;Application",<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Long:&nbsp;&nbsp;`Example&nbsp;Go&nbsp;CLI&nbsp;Application`,<br /> &nbsp;&nbsp;&nbsp;}<br />)<br /><br />func Execute() {<br />&nbsp;&nbsp;&nbsp;&nbsp;<em>if</em> err&nbsp;:=&nbsp;rootCmd.Execute();&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fmt.Fprintln(os.Stderr,&nbsp;err)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;os.Exit(1)<br /> &nbsp;&nbsp;&nbsp;}<br />}<br /><br />func init() {<br /> &nbsp;&nbsp;&nbsp;rootCmd.CompletionOptions.DisableDefaultCmd&nbsp;=&nbsp;true<br />}</pre>
<p><code>rootCmd</code> değişkenimiz i&ccedil;erisinde bir <code>cobra.Command</code> t&uuml;r&uuml;nde struct tanımlıyoruz. Bu struct i&ccedil;erisinde uygulamamız &ccedil;alıştırıldığında yardım sayfasında &ccedil;ıkacak mesajlar vb. d&uuml;zenlenmektedir.</p>
<p><code>Execute()</code> fonksiyonu ise Cobra uygulamamızın &ccedil;alışmasını sağlayan kod bloğudur.</p>
<p><code>init</code> fonksiyonunda da uygulama her &ccedil;alıştırıldığında yapılacak işlemler tanımlanacaktır.</p>
<p>Gelin bir de ilk komutumuzu ekleyelim.</p>
<pre>package&nbsp;cmd<br /><br />import (<br /> &nbsp;&nbsp;&nbsp;"fmt"<br /><br /> &nbsp;&nbsp;&nbsp;"github.com/spf13/cobra"<br />)<br /><br />func init() {<br /> &nbsp;&nbsp;&nbsp;rootCmd.AddCommand(exampleCmd)<br />}<br /><br />func example() {<br /> &nbsp;&nbsp;&nbsp;fmt.Println("My&nbsp;example&nbsp;output")<br />}<br /><br />var (<br /> &nbsp;&nbsp;&nbsp;exampleCmd&nbsp;=&nbsp;&amp;cobra.Command{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Use:&nbsp;&nbsp;&nbsp;"example",<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Short:&nbsp;"Example&nbsp;command",<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Long:&nbsp;&nbsp;"Example&nbsp;command",<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Run:&nbsp;func(cmd&nbsp;*cobra.Command,&nbsp;args&nbsp;[]string)&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;example()<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br /> &nbsp;&nbsp;&nbsp;}<br />)</pre>
<p>Yukarıda g&ouml;r&uuml;ld&uuml;ğ&uuml; &uuml;zere <code>exampleCmd</code> değişkeninin i&ccedil;eriğine bir de <code>Run</code> diye bir anahtar ekledik. Bu Run anahtarı komut &ccedil;alıştırıldığında y&uuml;r&uuml;t&uuml;lecek fonksiyon(ları) eklediğimiz ve arg&uuml;manları yakaladığımız kısım. Ben &ouml;rnek olarak example fonksiyonunu &ccedil;ağırmasını istedim.</p>
<p>Example fonksiyonunda ise "My example output" diye bir &ccedil;ıktı verdim.</p>
<p>Ardından komutun Cobra sistemine eklenebilmesi i&ccedil;in <code>init</code> fonksiyonum i&ccedil;inde <code>rootCmd</code> i&ccedil;erisine <code>addCommand</code> fonksiyonunu kullanarak ekledim.</p>
<p>Şimdi denemek i&ccedil;in <code>go build</code> komutunu &ccedil;alıştırıyorum ve klas&ouml;r&uuml;m&uuml;z i&ccedil;erisinde <code>go-cli-application</code> diye bir &ccedil;alıştırılabilir dosya oluştu. Bu dosyayı &ccedil;alıştırdığımda yardım sayfası g&ouml;r&uuml;necek, <code>go-cli-application example</code> diye &ccedil;alıştırdığımızda ise fonksiyonumuz &ccedil;alışacaktır.</p>
<p>Temel olarak bir Go CLI uygulaması bu şekilde oluşturulabilir. Geri kalanı sizin yaratıcılığınıza kalmış, ister veritabanına bağlanıp &ccedil;eşitli sorgular &ccedil;alıştıran bir uygulama, isterseniz sistem dosyalarını değiştiren bir program yazabilirsiniz.</p>
<p>Sorularınız i&ccedil;in yorum b&ouml;l&uuml;m&uuml;n&uuml; kullanabilirsiniz. İyi &ccedil;alışmalar dilerim :)</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Pardus WSL Kurulumu]]></title>
            <link>https://dogukan.dev/pardus-wsl-kurulumu</link>
            <guid>https://dogukan.dev/pardus-wsl-kurulumu</guid>
            <pubDate>Sat, 08 May 2021 20:52:49 GMT</pubDate>
            <description><![CDATA[
Pardus işletim sistemi için daha önce WSL programı oluşturulmadığını gördüm ve bu sebepten ötürü Pardus işletim sistemini WSL paketi haline getirdim. Bu işlem için Microsoft'un WSL-DistroStarter paketini kullanarak özel bir...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/pardus.jpg" alt="Pardus" width="1131" height="707" /></p>
<p>Pardus işletim sistemi i&ccedil;in daha &ouml;nce WSL programı oluşturulmadığını g&ouml;rd&uuml;m ve bu sebepten &ouml;t&uuml;r&uuml; Pardus işletim sistemini WSL paketi haline getirdim. Bu işlem i&ccedil;in Microsoft'un WSL-DistroStarter paketini kullanarak &ouml;zel bir exe derledim.</p>
<p>WSL'i daha &ouml;nce kullanmadıysanız ya da kurmadıysanız <a title="WSL2 ile Windows &Uuml;zerinde Linux Kullanmak" href="/wsl2-ile-windows-uezerinde-linux-kullanmak.html" target="_blank" rel="noopener">WSL2 ile Windows &Uuml;zerinde Linux Kullanmak</a> isimli makalemi okuyarak temel bilgileri edinebilir, nasıl kuracağınızı &ouml;ğrenebilirsiniz. Şimdi Pardus WSL nasıl kurulur onu anlatalım.</p>
<p>&Ouml;ncelikle <a href="https://github.com/dogukanoksuz/PardusWSL/releases">Releases &middot; dogukanoksuz/PardusWSL (github.com)</a> adresinden <em>Pardus.exe</em> ve <em>install.tar.gz</em> dosyasını indirip aynı klas&ouml;re koyunuz. Klas&ouml;r&uuml;n dosya izinlerinin doğru olduğuna emin olunuz. Masa&uuml;st&uuml;n&uuml;z veya C:\ diskinizin ana dizini elverişli olabilir.&nbsp;</p>
<p><img src="/images/1/wsl-0.jpg" alt="Pardus WSL" width="958" height="228" /></p>
<p>Yukarıda g&ouml;rd&uuml;ğ&uuml;n&uuml;z şekilde yerleştirdiyseniz Pardus.exe dosyasını &ccedil;alıştırabilirsiniz.</p>
<p><img src="/images/1/wsl-1.jpg" alt="Pardus WSL" width="975" height="496" /></p>
<p>Yukarıdaki mesajı g&ouml;rd&uuml;ğ&uuml;n&uuml;zde işlemin bitmesini bekleyiniz, biraz s&uuml;recektir.</p>
<p><img src="/images/1/wsl-2.jpg" alt="Pardus WSL" width="983" height="504" /></p>
<p>Kullanıcı adınız ve şifreniz sorulacak, onları oluşturduktan sonra WSL 1 s&uuml;r&uuml;m&uuml;nden WSL 2 s&uuml;r&uuml;m&uuml;ne y&uuml;kseltmemiz gerekiyor.</p>
<p><img src="/images/1/wsl-4.jpg" alt="Pardus WSL" width="1112" height="331" /></p>
<p>G&ouml;rd&uuml;ğ&uuml;n&uuml;z &uuml;zere <code>wsl --set-version Pardus 2</code> komutunu yazarak WSL 2 y&uuml;kseltme işlemini başarıyla tamamladım. WSL 2 daha stabil bir kullanım sunmaktadır.&nbsp;</p>
<p>Sonrasında kurduğumuz dağıtıma ulaşmak i&ccedil;in Pardus.exe dosyasını &ccedil;alıştırabiliriz veya Microsoft Store &uuml;zerinden Windows Terminal kurulumu yapabilirsiniz. İşlem bittikten sonra aşağıdaki şekilde sorunsuz Pardus işletim sistemini kullanabilirsiniz.</p>
<p><img src="/images/1/wsl-3.jpg" alt="Pardus WSL" width="979" height="406" /></p>
<p>Eğer bir problem g&ouml;r&uuml;rseniz GitHub reposu &uuml;zerinde issue a&ccedil;arsanız &ccedil;&ouml;zmeye &ccedil;alışırım. Okuduğunuz i&ccedil;in teşekk&uuml;r eder, iyi &ccedil;alışmalar dilerim.</p>
<p><strong><br />13.05.2021 G&uuml;ncelleme:</strong></p>
<p>Locale hatasının &ccedil;&ouml;z&uuml;m&uuml; i&ccedil;in aşağıdaki yolu takip edebilirsiniz.</p>
<p>/etc/default/locale dosyasını sudo ile a&ccedil;ıp i&ccedil;eriğini aşağıdaki ile değiştirin:</p>
<div class="snippet-clipboard-content position-relative">
<pre>LC_CTYPE="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
LANG="en_US.UTF-8"
</pre>
</div>
<p>ve şu komutu kullanın:</p>
<div class="snippet-clipboard-content position-relative">
<pre>sudo dpkg-reconfigure locales
</pre>
</div>
<p>ve aşağıdaki komutla WSL'i yeniden başlatın:</p>
<div class="snippet-clipboard-content position-relative">
<pre>wsl.exe --shutdown</pre>
<p>&nbsp;</p>
<p><strong>Systemd Aktifleştirme Y&ouml;ntemi</strong></p>
<p>DamionGans bir systemd aktifleştirme scripti oluşturmuş. Temiz kurulumlarda kullanınız ve WSL2'ye ge&ccedil;tiğinizden emin olun.<br /><a href="https://github.com/DamionGans/ubuntu-wsl2-systemd-script">https://github.com/DamionGans/ubuntu-wsl2-systemd-script</a><br />Yukarıdaki klas&ouml;r&uuml; klonlayın ve ardından sudo ile --force arg&uuml;manını girerek &ccedil;alıştırın.</p>
<p>&nbsp;</p>
</div>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Laravel 8 Kullanıcı Tipleri Oluşturmak ve Girişini Ayırmak]]></title>
            <link>https://dogukan.dev/laravel-8-kullanici-tipleri-olusturmak-ve-girisini-ayirmak</link>
            <guid>https://dogukan.dev/laravel-8-kullanici-tipleri-olusturmak-ve-girisini-ayirmak</guid>
            <pubDate>Sun, 04 Apr 2021 00:47:58 GMT</pubDate>
            <description><![CDATA[
Laravel 8 üzerinde bildiğiniz üzere gelen hazır bir auth paketi mevcut. Bu auth paketi üzerinde rollere göre giriş sonrası panelleri değiştiremiyoruz. Bunun için kodlarda değişiklik yaparak projemizi uygun hale getirmemiz g...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5dd587a6e06f8.jpg" alt="" width="740" height="345" /></p>
<p>Laravel 8 &uuml;zerinde bildiğiniz &uuml;zere gelen hazır bir auth paketi mevcut. Bu auth paketi &uuml;zerinde rollere g&ouml;re giriş sonrası panelleri değiştiremiyoruz. Bunun i&ccedil;in kodlarda değişiklik yaparak projemizi uygun hale getirmemiz gerekmektedir.</p>
<h2>Laravel projesi nasıl oluşturulur?</h2>
<p>İlk &ouml;nce Laravel projemizi oluşturmamız gerekmektedir. Bu işlem i&ccedil;in aşağıdaki komutları terminalden &ccedil;alıştırabilirsiniz.</p>
<pre>$ curl -s https://laravel.build/example-app | bash</pre>
<p>example-app kısmı yerine kendi uygulama isminizi yazabilirsiniz. Projeniz o şekilde oluşacaktır. Ardından Laravel Breeze yani temel auth paketini ekleyelim. Bunun i&ccedil;in composer&rsquo;a ihtiyacımız var.</p>
<pre>$ composer require laravel/breeze &ndash;dev</pre>
<p>Bu komutun ardından breeze paketimizi sisteme ekledik ancak kurulum halen bitmedi. Şu komutları da giriyoruz. Tabii ki &ouml;ncesinde .env dosyanızı veritabanına bağlamayı unutmayınız. Eğer sisteminizde MySQL/MariaDB kurulumu mevcut değil ise sitemdeki Devilbox kurulumu makalesine bakabilir veya Laravel Sail kullanımını araştırabilirsiniz.</p>
<pre>$ php artisan breeze:install<br />$ npm install<br />$ npm run dev<br />$ php artisan migrate</pre>
<p>Bu kurulumları yaptıktan sonra Laravel uygulamamızda sorunsuz şekilde /register, /login gibi rotaların otomatik tanımlandığını g&ouml;receksiniz.</p>
<h2>Laravel ile kullanıcı tiplerini ayırmak</h2>
<p>Kullanıcılarımızın tiplerini veritabanı &uuml;zerinde belirleyelim. Doğru yol bu olmasa da ben anlatımda anlaşılır ve kolay olması i&ccedil;in direkt users tablosu &uuml;zerine ekleme yapacağım ancak optimal yol farklı bir tablo &uuml;zerinde user_roles tarzı bir tablo oluşturup model &uuml;zerinden &ccedil;ağırma işlemini yapmaktır. Bu yol ile default entryler i&ccedil;in ekstradan bir s&uuml;tun oluşturmamış olacaksınız.</p>
<p>Users tablosuna ben user_type isminde bir kısım ekledim. Database/migrations klas&ouml;r&uuml;n&uuml;z altından users dosyanızı bulunuz ve password altına aşağıdaki kod satırını ekleyiniz.</p>
<pre>$table-&gt;string('user_type')-&gt;default('customer');</pre>
<p>Benim sistemimde customer, booster ve superuser tipinde &uuml;&ccedil; kullanıcı olacağı i&ccedil;in default kısmını customer olarak tanımladım. Siz bu tipleri projeniz i&ccedil;in kendinize g&ouml;re belirlemelisiniz. Bu migration değişikliği sonrası şu komutu &ccedil;alıştırın.</p>
<pre>$ php artisan migrate:refresh</pre>
<p>Şimdi rotalarımızı oluşturmamız gerekiyor. Ben şu şekilde oluşturmayı tercih ettim.</p>
<pre>Route::get('/customer', function () {<br /> &nbsp;&nbsp; return view('dashboard');<br />})-&gt;middleware(['auth'])-&gt;name('customer');<br /><br />Route::get('/booster', function () {<br /> &nbsp;&nbsp; return view('dashboard');<br />})-&gt;middleware(['auth', 'role:booster'])-&gt;name('booster');<br /><br />Route::get('/superuser', function () {<br /> &nbsp;&nbsp; return view('dashboard');<br />})-&gt;middleware(['auth', 'role:superuser'])-&gt;name('superuser');</pre>
<p>G&ouml;rd&uuml;ğ&uuml;n&uuml;z &uuml;zere kodlarımızda roller i&ccedil;in &ouml;zel bir middleware tanımlamışım. Hemen oluşturalım.</p>
<pre>$ php artisan make:middleware CheckIfHasRole</pre>
<p>Oluşan dosyamızın i&ccedil;eriğini aşağıdaki gibi tanımlayalım.</p>
<pre>public function handle(Request $request, Closure $next, $type)<br />{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (auth()-&gt;check() &amp;&amp; auth()-&gt;user()-&gt;user_type == "superuser") {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return $next($request);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (auth()-&gt;check() &amp;&amp; auth()-&gt;user()-&gt;user_type == $type) {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $next($request);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return redirect(route('index'))-&gt;withErrors([$type. ' erişiminiz yok.', 'Kullanıcı: ' . auth()-&gt;user()-&gt;id]);<br />}</pre>
<p>Ardından App/Http/Kernel.php dosyamızı a&ccedil;alım. $routeMiddleware değişkenine yeni bir key, value ekleyelim.</p>
<pre>'role' =&gt; \App\Http\Middleware\CheckIfHasRole::class,</pre>
<p>Bu işlemi yaptıktan sonra rotalarımızın sorunsuz &ccedil;alıştığından emin olalım. Denediğinizde bir problem olmaması gerekiyor. Şimdi login olduğumuzda her seferinde /dashboard altına giriş yapmaya &ccedil;alıştığını farkedeceksiniz. Bunu da d&uuml;zeltmemiz gerekiyor. Bunun i&ccedil;in <em>App/Providers/RouteServiceProvider.php</em> dosyasını a&ccedil;alım.</p>
<p>Bu dosya i&ccedil;erisinde şu satırın altına kendi fonksiyonumuzu yazacağız.</p>
<pre>public const HOME = '/customer';</pre>
<pre>public static function redirectTo() {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (auth()-&gt;user()-&gt;user_type == "booster") {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "/booster";<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (auth()-&gt;user()-&gt;user_type == "superuser") {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "/superuser";<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "/customer";<br />}</pre>
<p>Şeklinde fonksiyonumuzu ekleyelim. Bu fonksiyon rollerimize g&ouml;re y&ouml;nlenmemiz gereken adresleri d&uuml;zenleyecek. Şimdi fonksiyonu işleme sokmak i&ccedil;in <em>App/Http/Controllers/Auth</em> altındaki t&uuml;m dosyalarda <code>RouteServiceProvider::HOME</code> yerine <code>RouteServiceProvider::redirectTo()</code> yazmamız gerekmekte. Bu değişiklik i&ccedil;in VSCode ile projenizde home değişkenini aratıp hepsini redirectTo() ile değiştirmesini sağlayabilirsiniz.</p>
<p>Bu işlemlerin hepsini doğru şekilde yaptığınızda Laravel 8 &uuml;zerinde kullanıcı tiplerinin girişini birbirinden ayırmış olacaksınız. Sorularınız i&ccedil;in yorum b&ouml;l&uuml;m&uuml;n&uuml; kullanabilirsiniz.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[TailwindCSS Başlangıç Projesi Oluşturmak]]></title>
            <link>https://dogukan.dev/tailwindcss-baslangic-projesi-olusturmak</link>
            <guid>https://dogukan.dev/tailwindcss-baslangic-projesi-olusturmak</guid>
            <pubDate>Mon, 22 Mar 2021 04:19:49 GMT</pubDate>
            <description><![CDATA[
Merhaba, kendi websitemi de oluşturduğum CSS frameworkü TailwindCSS’yi kurmak ve konfigüre etmek biraz zor. Bu sebeple taban (starter) proje oluşturmayı göstermek için bir makale yazmak istedim. Öncelikle TailwindCSS nedir ...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/tailwind.jpg" alt="Tailwind" width="1000" height="420" /></p>
<p>Merhaba, kendi websitemi de oluşturduğum CSS framework&uuml; <strong>TailwindCSS</strong>&rsquo;yi kurmak ve konfig&uuml;re etmek biraz zor. Bu sebeple taban (starter) proje oluşturmayı g&ouml;stermek i&ccedil;in bir makale yazmak istedim. &Ouml;ncelikle TailwindCSS nedir bilmeyen dostlarımız i&ccedil;in o kısma değinelim.</p>
<h2>TailwindCSS Nedir?</h2>
<p>TailwindCSS Laravel ekibinden insanların g&ouml;n&uuml;ll&uuml; olarak geliştirdiği a&ccedil;ık kaynaklı bir CSS yardımcı ara&ccedil; kitidir. Bootstrap ve Foundationdan farklı olarak UI kit değildir, komponent bazlı değildir. Yardımcı classları kullanarak komponentleri sizin geliştirmeniz beklenmektedir. &Ouml;rnek olarak şu an websitemin kaynak kodlarını incelerseniz TailwindCSS baz classları kullanarak oluşturulduğunu g&ouml;r&uuml;rs&uuml;n&uuml;z. HTML/CSS konusuna hakim olan dostlarım zaten kodları g&ouml;rd&uuml;ğ&uuml;nde olayı anlayacaktır.</p>
<h2>TailwindCSS Projesi Nasıl Oluşturulur?</h2>
<p>İlk &ouml;nce klas&ouml;r oluşturup npmi başlatmalıyız, ardından gereken paketleri development ve main branch olarak ayırarak kurulumunu ger&ccedil;ekleştirmeliyiz.</p>
<pre>$ mkdir tailwindstarter<br />$ npm init<br />$ npm install tailwindcss@latest postcss@latest autoprefixer@latest<br />$ npm install -D concurrently@latest cross-env@latest cssnano@latest live-server@latest postcss-cli@latest</pre>
<p>Bu adımlardan sonra <em>postcss.config.js</em> dosyamızı oluşturuyoruz ve i&ccedil;eriğini aşağıdaki gibi yapıyoruz.</p>
<pre>$ nano postcss.config.js</pre>
<pre>module.exports = {<br />  plugins: [<br /> &nbsp;&nbsp; require("tailwindcss"),<br /> &nbsp;&nbsp; require("autoprefixer"),<br /> &nbsp;&nbsp; ...(process.env.NODE_ENV === "production" ? [require("cssnano")] : []),<br />  ],<br />};</pre>
<p>Bu dosyayı oluşturduktan sonra aşağıdaki komutu girerek tailwind config dosyamızı oluşturuyoruz.</p>
<pre>$ npx tailwind init</pre>
<p>Ardından <em>tailwind.config.js</em> dosyamızın i&ccedil;eriğini aşağıdakine uygun olarak d&uuml;zenleyelim.</p>
<pre>module.exports = {<br />  purge: [<br /> &nbsp;&nbsp; './public/**/*.html',<br />  ],<br />  darkMode: false, // or 'media' or 'class'<br />  theme: {<br /> &nbsp;&nbsp; extend: {},<br />  },<br />  variants: {<br /> &nbsp;&nbsp; extend: {},<br />  },<br />  plugins: [],<br />}</pre>
<p>Bu adımda kendi dosyanızda b&uuml;y&uuml;k ihtimalle purge kısmını sizinkinden farklı g&ouml;receksiniz. Bu kısmın sebebi <strong>PostCSS preprocessor</strong> kullandığımızdan production buildi aldığımızda kullanmadığımız classların &ccedil;ıkarılmasını sağlayacaktır. &Ouml;rnek kullanımı aynı şekilde kendi sitemdeki <em>build.css</em> dosyasını g&ouml;r&uuml;nt&uuml;leyerek g&ouml;rebilirsiniz. Tailwind salt hali ile 2-3 MB arası bir boyuttayken sitemde production build sonrası 46kB boyutuna d&uuml;şm&uuml;şt&uuml;r.</p>
<p>Bu işlemlerin ardından yaptığımız işlemler dışında aşağıdaki klas&ouml;r yapısını oluşturalım.</p>
<pre>├── package.json<br />├── package-lock.json<br />├── postcss.config.js<br />├── public<br />│&nbsp;&nbsp; └── index.html<br />├── src<br />│&nbsp;&nbsp; ├── css<br />│&nbsp;&nbsp; │&nbsp;&nbsp; └── app.css<br />│&nbsp;&nbsp; └── js<br />│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; └── app.js<br />└── tailwind.config.js</pre>
<p>Klas&ouml;r yapımızı bu hale getirmemizin sebebi src klas&ouml;r&uuml; i&ccedil;erisinde css ve javascript dosyalarımızın birlikte derlenmemiş hali bulunmasıdır. Public klas&ouml;r&uuml;m&uuml;z i&ccedil;erisine dosyamızda değişiklik yaptık&ccedil;a s&uuml;rekli otomatik olarak development buildi derlenecektir.</p>
<p>src/css i&ccedil;erisinde oluşturduğunuz app.css dosyasını a&ccedil;ın ve i&ccedil;eriğini aşağıdaki ile değiştirin. Bu değişiklik sayesinde Tailwind k&uuml;t&uuml;phanesinin stillerini projenize ekleyeceksiniz.</p>
<pre>@tailwind base;<br />@tailwind components;<br />@tailwind utilities;</pre>
<p>Son olarak projemizi nasıl &ccedil;alıştıracağız dediğinizi duyar gibiyim. package.json dosyamızı d&uuml;zenleyip &ccedil;alıştırma scriptlerimizi girelim ve kolay şekilde projemizi tekrar tekrar &ccedil;alıştırabilelim.</p>
<pre>"scripts": {<br /> &nbsp;&nbsp; "serve": "cross-env NODE_ENV=development concurrently \"postcss src/css/app.css -o public/css/build.css --watch\" &gt; "development": "cross-env NODE_ENV=development postcss src/css/app.css -o public/css/build.css",<br /> &nbsp;&nbsp; "production": "cross-env NODE_ENV=production postcss src/css/app.css -o public/css/build.css"<br />},</pre>
<p>Burada eklediğiniz kodda live browser edit ile kullanmak i&ccedil;in <code>npm run serve</code>, production buildi alabilmek i&ccedil;in ise <code>npm run production</code> yazmanız gerekmektedir.</p>
<p>Starter projesini bu kadar uğraşmadan kullanmak istiyorsanız makaleyi hazırlarken oluşturduğum hazır halini github adresime ekledim. <a href="https://github.com/dogukanoksuz/tailwind-starter">dogukanoksuz/tailwind-starter: TailwindCSS + PostCSS Starter Pack (github.com)</a> adresinden ulaşabilirsiniz.</p>
<p>Sorularınız i&ccedil;in aşağıdaki yorum b&ouml;l&uuml;m&uuml;n&uuml; kullanabilirsiniz. Okuduğunuz i&ccedil;in teşekk&uuml;r ederim.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[WSL2 4294967295 Hata Kodu Çözümü]]></title>
            <link>https://dogukan.dev/wsl2-4294967295-hata-kodu-cozumu</link>
            <guid>https://dogukan.dev/wsl2-4294967295-hata-kodu-cozumu</guid>
            <pubDate>Sat, 13 Feb 2021 02:56:15 GMT</pubDate>
            <description><![CDATA[WSL2 (Windows Subsystem for Linux) üzerinde 4294967295 kodu ile bir hata mı alıyorsunuz? Bu hata ile bugün karşılaştığım için çözüm yollarını sizinle paylaşacağım.
BIOS ayarlarınız doğru mu?
Bios üzerinden ayarlarınızı kont...]]></description>
            <content:encoded><![CDATA[<p>WSL2 (Windows Subsystem for Linux) &uuml;zerinde 4294967295 kodu ile bir hata mı alıyorsunuz? Bu hata ile bug&uuml;n karşılaştığım i&ccedil;in &ccedil;&ouml;z&uuml;m yollarını sizinle paylaşacağım.</p>
<h2>BIOS ayarlarınız doğru mu?</h2>
<p>Bios &uuml;zerinden ayarlarınızı kontrol ediniz. <strong>VT-d</strong>, <strong>VT-x</strong> ve <strong>Virtualization Technology</strong> ayarlarının aktif hale getirilmesi gerekmektedir. Aksi halde <strong>WSL2</strong> başlamayacaktır.</p>
<h2>Windows &ouml;zelliklerini kontrol edin</h2>
<p>Windows Features veya Windows &ouml;zellikleri diye adlandırabileceğimiz kısımdan ayarlarınızı kontrol ediniz.<strong> Virtual Machine Platform</strong>, <strong>Windows Hypervisor Platform</strong> ve <strong>Windows Subsystem for Linux</strong> se&ccedil;eneklerinin işaretli olduğuna emin olunuz.</p>
<p><img src="/images/1/602685b2d4200.jpg" alt="Windows Features" border="0" /></p>
<h2>VMware &uuml;r&uuml;nleri sisteminizde kurulu mu?</h2>
<p>Eğer sisteminizde VMware &uuml;r&uuml;nleri kurulu ise şu anda bilinen bir &ccedil;akışma s&ouml;z konusu. Yukarıdaki adımları kontrol ettikten sonra bu adıma d&ouml;n&uuml;n ve sisteminizdeki VMware &uuml;r&uuml;nlerini kaldırmayı deneyiniz.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[WSL2 ile Docker Kullanmak]]></title>
            <link>https://dogukan.dev/wsl2-ile-docker-kullanmak</link>
            <guid>https://dogukan.dev/wsl2-ile-docker-kullanmak</guid>
            <pubDate>Thu, 15 Oct 2020 04:35:19 GMT</pubDate>
            <description><![CDATA[Bugün WSL2 ile Docker containerlarımızı nasıl kurarız bu konuyu konuşacağız. Farkettiyseniz WSL2 üzerinde çalışan linux systemd tabanlı değil. Bu sebepten ötürü Docker containerlarınızı servis olarak çalıştıramayacaksınız. Bu...]]></description>
            <content:encoded><![CDATA[<p>Bug&uuml;n <strong>WSL2</strong> ile <strong>Docker</strong> containerlarımızı nasıl kurarız bu konuyu konuşacağız. Farkettiyseniz WSL2 &uuml;zerinde &ccedil;alışan <strong>linux</strong> <strong>systemd</strong> tabanlı değil. Bu sebepten &ouml;t&uuml;r&uuml; Docker containerlarınızı servis olarak &ccedil;alıştıramayacaksınız. Bunun i&ccedil;in Docker&rsquo;in bir &ccedil;&ouml;z&uuml;m&uuml; var. Eğer sisteminizde WSL2 kurulu değil ise <a href="/wsl2-ile-windows-uezerinde-linux-kullanmak.html" target="_blank" rel="noopener">WSL2 ile Windows &Uuml;zerinde Linux Kullanmak</a> makalemizi takip edin ve ardından bu makaleyi takip etmeye başlayın.</p>
<h2>Windows Docker Desktop Kurulumu</h2>
<p>&Ouml;ncelikle <a title="Docker Desktop for Windows" href="https://hub.docker.com/editions/community/docker-ce-desktop-windows/" target="_blank" rel="noopener">bu adresten</a> Docker Desktop for Windows programını indirelim. Ardından kurulum işlemini yapalım <em>-kısaca t&uuml;m se&ccedil;eneklere next diyebilirsiniz, adware/d&uuml;zenlenmesi gereken ayar bulunmamakta-</em>.</p>
<h2>&nbsp;</h2>
<h2>Docker Desktop WSL2 Ayarları</h2>
<p>Ayarları yapabilmek i&ccedil;in &ouml;ncelikle Docker Desktop uygulamasını a&ccedil;ınız. Sağ alttaki balina logosuna sağ tıklayıp Dashboard se&ccedil;eneğini se&ccedil;iniz. Aşağıda g&ouml;sterilen ayarları sırası ile yapınız.</p>
<p><img src="/images/1/5f8735494996b.jpg" alt="Docker WSL2 Ayarları" border="0" /></p>
<p>Uygulamamızı a&ccedil;tıktan sonra ayarlar simgesine tıklayalım. Ardından General men&uuml;s&uuml; altından Use the WSL 2 based engine se&ccedil;eneğini se&ccedil;elim.&nbsp;</p>
<p>&nbsp;</p>
<p><img src="/images/1/5f873549498a9.jpg" alt="Docker WSL 2 ile birlikte kullanmak" border="0" /></p>
<p>Resources men&uuml;s&uuml;ne tıklayalım. Buradan <strong>WSL INTEGRATION</strong> se&ccedil;eneğini se&ccedil;elim. &Ccedil;ıkan ayarlardan "<strong>Enable integration with my default WSL distro</strong>" se&ccedil;eneğini işaretleyip alttaki distrolar i&ccedil;in gereken tikleri aktif hale getirelim.&nbsp;</p>
<p>Bu işlemleri tamamladıktan sonra Apply &amp; Restart d&uuml;ğmesine tıklayarak ayarlarımızı kaydedelim. Docker Desktop yeniden başlayacaktır. Bu işlemin ardından kontrol edebilirsiniz. Gelin beraber bakalım.</p>
<p>&nbsp;</p>
<h3>&Ccedil;alışıyor mu deneyelim 😊</h3>
<p><img src="/images/1/5f8735494997f.jpg" alt="WSL 2 Docker" border="0" /></p>
<p>G&ouml;rd&uuml;ğ&uuml;n&uuml;z gibi &ouml;ncesi sonrası farkını g&ouml;rm&uuml;ş olduk. Sorularınızı yorum b&ouml;l&uuml;m&uuml;nden y&ouml;neltebilirsiniz. İyi &ccedil;alışmalar dilerim.</p>
<p>&nbsp;</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Socket.IO Emit İşlemleri Kopya Kağıdı]]></title>
            <link>https://dogukan.dev/socket-io-emit-islemleri-kopya-kagidi</link>
            <guid>https://dogukan.dev/socket-io-emit-islemleri-kopya-kagidi</guid>
            <pubDate>Fri, 09 Oct 2020 23:47:07 GMT</pubDate>
            <description><![CDATA[Bu makale Socket.IO websitesi üzerinde yayınlanmış emit cheatsheet(kopya kağıdı) çevirisidir. Anlaşılabilir olması için yayınlamak istedim.
 
Cliente mesaj gönderme işlemi
socket.emit('hello', 'can you hear me?', 1, 2, 'ab...]]></description>
            <content:encoded><![CDATA[<p>Bu makale Socket.IO websitesi &uuml;zerinde yayınlanmış emit cheatsheet(kopya kağıdı) &ccedil;evirisidir. Anlaşılabilir olması i&ccedil;in yayınlamak istedim.</p>
<p>&nbsp;</p>
<p>Cliente mesaj g&ouml;nderme işlemi</p>
<pre>socket.emit('hello', 'can you hear me?', 1, 2, 'abc');</pre>
<p>&nbsp;</p>
<p>T&uuml;m clientlere g&ouml;nderim yapmak</p>
<pre>socket.broadcast.emit('broadcast', 'hello friends!');</pre>
<p>&nbsp;</p>
<p>Game isimli odadaki t&uuml;m clientlere g&ouml;nderim yapmak</p>
<pre>socket.to('game').emit('nice game', "let's play a game");</pre>
<p>&nbsp;</p>
<p>Game1 ve/veya Game2 odasındaki t&uuml;m clientlere g&ouml;nderim yapmak</p>
<pre>socket.to('game1').to('game2').emit('nice game', "let's play a game (too)");</pre>
<p>&nbsp;</p>
<p>Game odasındaki t&uuml;m clientlere <strong>g&ouml;nderici dahil</strong> g&ouml;nderim yapmak</p>
<pre>io.in('game').emit('big-announcement', 'the game will start soon');</pre>
<p>&nbsp;</p>
<p>myNamespace namespacei i&ccedil;erisindeki herkese <strong>g&ouml;nderici dahil</strong> g&ouml;nderim yapmak</p>
<pre>io.of('myNamespace').emit('bigger-announcement', 'the tournament will start soon');</pre>
<p>&nbsp;</p>
<p>Bir namespace i&ccedil;erisindeki odaya <strong>g&ouml;nderici dahil</strong> g&ouml;nderim yapmak</p>
<pre>io.of('myNamespace').to('room').emit('event', 'message');</pre>
<p>&nbsp;</p>
<p>Eğer biliyorsanız socketId ile g&ouml;nderim yapmanızı sağlar (&ouml;zel mesaj i&ccedil;in kullanılabilir)</p>
<pre>io.to(socketId).emit('hey', 'I just met you');</pre>
<p>DİKKAT: io.to yerine socket.to yazarsanız &ccedil;alışmayacaktır ve odadaki herkese g&ouml;nderim yapacaktır. Ne yaptığınızdan eminseniz kullanın.</p>
<p>&nbsp;</p>
<p>D&ouml;n&uuml;ş alabildiğiniz mesaj g&ouml;nderimi yapmak</p>
<pre>socket.emit('question', 'do you think so?', function (answer) {});</pre>
<p>&nbsp;</p>
<p>Sıkıştırma olmadan g&ouml;nderim yapmak</p>
<pre>socket.compress(false).emit('uncompressed', "that's rough");</pre>
<p>&nbsp;</p>
<p>Eğer client mesaj almaya hazır değilse ge&ccedil;ici mesajlar g&ouml;ndermek</p>
<pre>socket.volatile.emit('maybe', 'do you really need it?');</pre>
<p>&nbsp;</p>
<p>Node &uuml;zerindeki t&uuml;m clientlere g&ouml;nderim yapmak (eğer &ccedil;oklu socket kullanıyorsanız)</p>
<pre>io.local.emit('hi', 'my lovely babies');</pre>
<p>&nbsp;</p>
<p>Sunucuya bağlı t&uuml;m clientlere mesaj g&ouml;nderimi yapmak</p>
<pre>io.emit('an event sent to all connected clients');</pre>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Node.js ile Socket.IO Kullanımı]]></title>
            <link>https://dogukan.dev/node-js-ile-socket-io-kullanimi</link>
            <guid>https://dogukan.dev/node-js-ile-socket-io-kullanimi</guid>
            <pubDate>Fri, 09 Oct 2020 23:40:47 GMT</pubDate>
            <description><![CDATA[
Socket.IO nedir?
Websocket altyapısı kullanan ancak websocket olmayan bir realtime enginedir. Tüm dillere community tarafından entegre edildiğinden ve esnek kullanım sağladığından modern altyapılar için kullanımı uygundur....]]></description>
            <content:encoded><![CDATA[<h2><img src="/images/1/5f8059a874514.jpeg" border="0" alt="Socket.IO" itemprop="image" /></h2>
<h2>Socket.IO nedir?</h2>
<p>Websocket altyapısı kullanan ancak websocket olmayan bir realtime enginedir. T&uuml;m dillere community tarafından entegre edildiğinden ve esnek kullanım sağladığından modern altyapılar i&ccedil;in kullanımı uygundur.</p>
<p>Socket.IO iki y&ouml;nl&uuml;, canlı zamanlı ve olaylar &uuml;zerine &ccedil;alışan bir iletişim sağlar. Her platformda, tarayıcıda veya cihazda g&uuml;venilirlik ve hız olarak eşitliği g&ouml;zetir.</p>
<p>&nbsp;</p>
<h2>Socket.IO&rsquo;ya başlangı&ccedil;</h2>
<p>&Ouml;ncelikle socket sunucumuzu oluşturarak başlamalıyız. Bu işlemi yapmak i&ccedil;in bir klas&ouml;r oluşturun i&ccedil;ine gidin ve ardından &ldquo;<code>npm init</code>&rdquo; komutunu &ccedil;alıştırınız, sorulan soruları istediğiniz şekilde yanıtlayarak projenizi oluşturun. Ardından gereken bağımlılıkların kurulumunu yapmamız gerekmekte. Bu işlem i&ccedil;in girmemiz gereken komut:</p>
<pre>npm install express socket.io node-fetch xml2js</pre>
<p>Bu komutun ardından express, socket.io, fetch ve xml2js kurulumu ger&ccedil;ekleşecektir. Makalemizde d&ouml;viz kurlarını 5 saniyede bir yenileyen bir proje oluşturacağız.</p>
<p>Klas&ouml;r&uuml;n&uuml;z i&ccedil;erisinde <strong>index.js</strong> dosyasını oluşturunuz ve i&ccedil;eriğini bu şekilde giriniz.</p>
<div>
<pre>const express = require("express")<br />const fetch = require("node-fetch")<br />const xml = require('xml2js').parseString<br />const app = express()<br />const http = require("http").createServer(app)<br />const io = require("socket.io")(http)<br />const&nbsp;ZAMAN&nbsp;=&nbsp;5000<br /><br />var&nbsp;Data<br /><br />let fetchCurrency = async () =&gt; {<br /> &nbsp;&nbsp;&nbsp;try&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;response&nbsp;=&nbsp;await&nbsp;fetch('https://www.tcmb.gov.tr/kurlar/today.xml')<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;text&nbsp;=&nbsp;await&nbsp;response.text()<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xml(text,&nbsp;(e,&nbsp;output)&nbsp;=&gt;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Data&nbsp;=&nbsp;output.Tarih_Date.Currency[0]<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br /> &nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(e)&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(e)<br /> &nbsp;&nbsp;&nbsp;}<br />}<br /><br />let currencyPoller = async () =&gt; {<br /> &nbsp;&nbsp;&nbsp;await&nbsp;fetchCurrency()<br /> &nbsp;&nbsp;&nbsp;setTimeout(async&nbsp;()&nbsp;=&gt;&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await&nbsp;fetchCurrency()<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(`Sunucuda&nbsp;data&nbsp;yenilendi.`)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currencyPoller()<br /> &nbsp;&nbsp;&nbsp;},&nbsp;ZAMAN);<br />}<br /><br />currencyPoller()<br /><br />http.listen(3000, () =&gt; {<br /> &nbsp;&nbsp;&nbsp;console.log(`I'm&nbsp;alive!`)<br />})</pre>
</div>
<p>&Ouml;ncelikle bu kodlarda ne yaptığımızdan bahsedelim. Başlangı&ccedil;ta paketlerimizi require ettik ve ne kadar s&uuml;rede bir yayın yapacağımızı bir sabit değişken ile belirttim. Ardından her kullanıcı i&ccedil;in verinin ayrı şekilde stream edilmesini istemediğimden &ouml;t&uuml;r&uuml; global bir değişken oluşturdum. fetchCurrency fonksiyonumuzda ise TCMB sunucularından XML verisini alıp bunu JSON&rsquo;a değiştirdim, ardından d&ouml;nen veriyi global değişkenimize aktardım. currencyPoller fonksiyonumuz ise bir &ouml;zyinelemeli fonksiyon olup kod &ccedil;alıştığından itibaren zaman sabitimiz kadar aralıklarla veri değişkenimizi yenileyecektir.</p>
<p>Şimdi &ldquo;node index.js&rdquo; yazarak &ccedil;alıştıralım ve terminalde &ldquo;I&rsquo;m alive&rdquo; yazısını g&ouml;rd&uuml;ğ&uuml;m&uuml;zden emin olalım. Yazıyı g&ouml;rd&uuml;kten sonra browserimizden localhost:3000 adresine gidip istek alabiliyor muyuz g&ouml;relim. Eğer iki işlemimiz de başarılı ise artık başlayabiliriz.</p>
<p>&nbsp;</p>
<h2>Canlı Zamanlı D&ouml;viz Kuru Kontrol&uuml; Yapımı</h2>
<h4>Node.js taraflı veri g&ouml;nderimi</h4>
<p>Yukarıda g&ouml;rd&uuml;ğ&uuml;n&uuml;z kısımda Node.js projemizin temelini oluşturmuştuk. Şimdi sokete yayın yapmamız i&ccedil;in gereken kısma başlayabiliriz.</p>
<pre>io.on("connection", (socket) =&gt; {<br /> &nbsp;&nbsp; console.log(`Yeni bir bağlantı yapıldı.`)<br /> &nbsp;&nbsp; let socketPoller = () =&gt; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socket.emit('currency-data', Data)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setTimeout(() =&gt; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socket.emit('currency-data', Data)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(`Bağlantıya veri g&ouml;nderimi yapıldı.`)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socketPoller()<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, ZAMAN);<br />&nbsp;&nbsp;&nbsp; }<br /><br /> &nbsp;&nbsp; socketPoller()<br /><br /> &nbsp;&nbsp; socket.on("disconnect", () =&gt; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(`Bağlantı koptu.`)<br /> &nbsp;&nbsp; })<br />})</pre>
<p>Bu kod bloğunu konuşarak başlayalım. G&ouml;z&uuml;m&uuml;ze &ccedil;arpması gereken kritik kısımlar <strong>io.on</strong> fonksiyonu, <strong>socket.emit</strong> fonksiyonlarıdır. <strong>io.on</strong> fonksiyonu bağlantı geldiğinde yapılacak eylemler dizisini tetikler. Buranın i&ccedil;erisine yazacağımız kodlar sadece ve sadece bağlantı geldiyse &ccedil;alışacaktır.</p>
<p><strong>socket.emit</strong> fonksiyonu ise ilk arg&uuml;man olarak bir emit ismi almaktadır. Bu ismi siz belirleyip karşı clientte de bu isme g&ouml;re dinleme yapacaksınız. İkinci arg&uuml;man ise g&ouml;ndermek istediğiniz veri olmalıdır. Kodda biraz &ouml;nce veri &ccedil;ekerken yaptığımız s&uuml;rekli g&ouml;nderim işlemini implemente ettik.</p>
<p>Bu kod bloğumuzu <strong>currencyPoller()</strong> satırının hemen ardına ekleyebiliriz. Şimdi clientte dinleme işlemine ge&ccedil;elim.</p>
<p>&nbsp;</p>
<h4>Client taraflı dinleme işlemi</h4>
<p>Client tarafında dinlemek i&ccedil;in &ouml;ncelikle veriler.html diye bir dosya oluşturunuz. Ardından soket kodumuzun alt kısmına şu satırları ekleyiniz.</p>
<pre>app.get('/', (req, res) =&gt; {<br /> &nbsp;&nbsp; res.sendFile(__dirname + '/veriler.html');<br />});</pre>
<p>Bu kod <strong>localhost:3000</strong> adresine eriştiğimizde veriler.html dosyasının render edilmesini sağlayacaktır. Ardından veriler.html dosyasının i&ccedil;eriğini aşağıdaki gibi d&uuml;zenleyiniz.</p>
<div>
<pre>&lt;!DOCTYPE html&gt;<br />&lt;html lang="en"&gt;<br /><br />&lt;head&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;meta&nbsp;charset="UTF-8"&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;meta&nbsp;name="viewport"&nbsp;content="width=device-width,&nbsp;initial-scale=1.0"&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;title&gt;Canlı&nbsp;zamanlı&nbsp;USD&nbsp;kuru&lt;/title&gt;<br />&lt;/head&gt;<br /><br />&lt;body&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;div&nbsp;id="currency"&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;/div&gt;<br /><br /> &nbsp;&nbsp;&nbsp;&lt;script&nbsp;src="/socket.io/socket.io.js"&gt;&lt;/script&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;script&nbsp;src="https://code.jquery.com/jquery-3.4.1.min.js"&gt;&lt;/script&gt;<br /> &nbsp;&nbsp;&nbsp;&lt;script&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$(function&nbsp;()&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;socket&nbsp;=&nbsp;io();<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket.on('currency-data',&nbsp;function&nbsp;(data)&nbsp;{<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$("#currency").html(<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Dolar&nbsp;kuru:&nbsp;${data.ForexSelling}&lt;br&gt;Yenilendiği&nbsp;tarih:&nbsp;${new&nbsp;Date().getHours()}:${new&nbsp;Date().getMinutes()}:${new&nbsp;Date().getSeconds()}`<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br /> &nbsp;&nbsp;&nbsp;&lt;/script&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;</pre>
</div>
<p>
<script><br />        $(function () {<br />            var socket = io();<br />            socket.on('currency-data', function (data) {<br />                $("#currency").html(<br />                    `Dolar kuru: ${data.ForexSelling}<br>Yenilendiği tarih: ${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`<br />                )<br />            });<br />        });<br />    </script>
</p>
<p>Burada g&ouml;z&uuml;m&uuml;ze &ccedil;arpması gereken kısım script taglarımızın i&ccedil;erisidir. <strong>var socket = io()</strong> komutu ile soket bağlantımızı a&ccedil;tık. Ardından clientte currency-data verimizi <strong>socket.on</strong> komutunu kullanarak dinlemeye başladık. Dinlediğimiz veriyi basit bir şekilde jQuery kullanarak ekrana yazdırdım ve g&uuml;ncellendiği belli olsun diye saati ekledim.</p>
<p>Bu işlemler bittikten sonra node index.js yazınız ve browserinizden localhost:3000 adresini takip ediniz.</p>
<p>Projenin bitmiş halini şu adresten ziyaret edebilirsiniz: <a href="https://github.com/dogukanoksuz/realtime-currency">https://github.com/dogukanoksuz/realtime-currency</a></p>
<p>Eğer canlı halini videolu g&ouml;rmek istiyorsanız:</p>
<center><iframe src="https://www.youtube-nocookie.com/embed/oF5XII_7pG0" width="560" height="315" frameborder="0"></iframe></center>
<p>Anlatımımı takip ettiğiniz i&ccedil;in teşekk&uuml;r ederim. Eğer <strong>socket.io</strong>&rsquo;da ilerlemek istiyorsanız iyi bir başlangı&ccedil; yaptınız ancak buradaki bilgilerle sınırlı kalmayın! <a href="/socket-io-emit-islemleri-kopya-kagidi.html" target="_blank" rel="noopener"><strong>Emit cheatsheet</strong></a>&rsquo;e g&ouml;z atın, ayrıca T&uuml;rk&ccedil;e &ccedil;evirisi de sitemde mevcut! <a href="/socket-io-emit-islemleri-kopya-kagidi.html" target="_blank" rel="noopener">Buraya</a> tıklayarak gidebilirsiniz. <strong>namespace</strong> konusuna bakın, <strong>room</strong>lara bakın, clientten de servera nasıl mesaj g&ouml;nderebildiğinizi deneyin. Daha fazla detay &ouml;ğrenmek i&ccedil;in <a href="https://socket.io/docs/">https://socket.io/docs/</a> adresini ziyaret edebilirsiniz. Yazdığınız soket uygulamalarını test etmek i&ccedil;in ise <a href="https://socketserve.io">https://socketserve.io</a> adresini ziyaret edebilirsiniz. Sorularınızı yorum b&ouml;l&uuml;m&uuml;nde yanıtlıyor olacağım.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[PHP Spotify API Kullanımı]]></title>
            <link>https://dogukan.dev/php-spotify-api-kullanimi</link>
            <guid>https://dogukan.dev/php-spotify-api-kullanimi</guid>
            <pubDate>Wed, 29 Jul 2020 01:53:47 GMT</pubDate>
            <description><![CDATA[
Selamlar, uzun zamandır buraya yazı girmemişim. Bir süredir üzerinde çalıştığım PHP Spotify API'ın nasıl kullanıldığına dair bir Türkçe doküman tarzı bir şey hazırlamak istedim. Önden belirteyim, sorularınızı da yorum bölüm...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5f203bd5d0a31.jpg" alt="Spotify" border="0" /></p>
<p>Selamlar, uzun zamandır buraya yazı girmemişim. Bir s&uuml;redir &uuml;zerinde &ccedil;alıştığım PHP Spotify API'ın nasıl kullanıldığına dair bir T&uuml;rk&ccedil;e dok&uuml;man tarzı bir şey hazırlamak istedim. &Ouml;nden belirteyim, sorularınızı da yorum b&ouml;l&uuml;m&uuml;nde yanıtlayacağım. Bu sayede hem ben hem de siz &ouml;ğreneceksiniz. Temel olarak konuştuğumuza g&ouml;re konuşmaya başlayalım.<br /><br /></p>
<h3>PHP Spotify API i&ccedil;in gereksinimler:</h3>
<ul>
<li>PHP 7.4</li>
<li>Composer</li>
<li>Spotify &uuml;yeliği</li>
<li>biraz da programlama tutkusu :)</li>
</ul>
<p>&nbsp;</p>
<h2>Spotify Developer panelinde uygulama oluşturma:</h2>
<p>API'ımızı kullanabilmemiz i&ccedil;in Spotify developer panelinde uygulama oluşturmamız gerekiyor. Gelin beraber yapalım.</p>
<ul>
<li><a href="https://developer.spotify.com">https://developer.spotify.com</a> adresine girin ve hesabınızla giriş yapın.<br /><br /></li>
<li>Giriş yaptıktan sonra <a href="https://developer.spotify.com/dashboard/applications">https://developer.spotify.com/dashboard/applications</a> altına ilerleyelim ve yeşil <strong>"Create an app"</strong> d&uuml;ğmesine tıklayalım.<br /><br /></li>
<li><img src="/images/1/5f202a9113d56.jpg" alt="Spotify Create An App" border="0" /><br />İlk boşluğa uygulama adınızı, ikinci boşluğa da a&ccedil;ıklamanızı yazınız ardından tiklerin i&ccedil;eriğini okuyarak işaretleyin ve Create diyin :)<br /><br /></li>
<li><img src="/images/1/5f202b99319ba.jpg" alt="Spotify Client key" border="0" /><br />Bu iki keyi g&uuml;venli bir yere not alın, ileride API bağlantımızı yaparken ihtiyacımız olacak.<br /><br /></li>
</ul>
<h2>PHP Spotify API nasıl kullanılır?</h2>
<ul>
<li>&Ouml;ncelikle temel projemizi a&ccedil;acağız. Bir klas&ouml;r oluşturun, index.php dosyasını oluşturun.</li>
<li>Ardından terminal/cmd ekranını a&ccedil;ın, klas&ouml;re girdikten sonra <code>composer require jwilsson/spotify-web-api-php</code> yazın.</li>
<li>index.php dosyasının başına <!--?php require 'vendor/autoload.php'; ekleyin ve ardından uçmaya hazırız, şimdi anlatmaya başlayalım!</li>--></li>
</ul>
<p>&nbsp;</p>
<p><strong>Access token alıp sisteme bağlanmak:</strong></p>
<pre><span class="pl-k">$session = new SpotifyWebAPI\Session('CLIENT_ID', 'CLIENT_SECRET', 'REDIRECT_URI');<br />$api = new SpotifyWebAPI\SpotifyWebAPI(['auto_refresh' =&gt; true, 'auto_retry' =&gt; true]);<br />if</span> (<span class="pl-en">isset</span>(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-c1">_GET</span></span>[<span class="pl-s">'code'</span>])) {
    <span class="pl-s1"><span class="pl-c1">$</span>session</span>-&gt;<span class="pl-en">requestAccessToken</span>(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-c1">_GET</span></span>[<span class="pl-s">'code'</span>]);
    <span class="pl-s1"><span class="pl-c1">$</span>api</span>-&gt;<span class="pl-en">setAccessToken</span>(<span class="pl-s1"><span class="pl-c1">$</span>session</span>-&gt;<span class="pl-en">getAccessToken</span>());<br />    // $refresh_token = $session-&gt;getRefreshToken();
    <span class="pl-en">print_r</span>(<span class="pl-s1"><span class="pl-c1">$</span>api</span>-&gt;<span class="pl-en">me</span>());
} <span class="pl-k">else</span> {
    <span class="pl-s1"><span class="pl-c1">$</span>options</span> = [
        <span class="pl-s">'scope'</span> =&gt; [<br />            // bu kısım hakkında detaylı bilgi i&ccedil;in:<br />            // <a href="https://developer.spotify.com/documentation/general/guides/scopes/">https://developer.spotify.com/documentation/general/guides/scopes/</a>
            <span class="pl-s">'user-read-email'</span>,
        ],
    ];

    <span class="pl-en">header</span>(<span class="pl-s">'Location: '</span> . <span class="pl-s1"><span class="pl-c1">$</span>session</span>-&gt;<span class="pl-en">getAuthorizeUrl</span>(<span class="pl-s1"><span class="pl-c1">$</span>options</span>));
    <span class="pl-en">die</span>();
}</pre>
<p>Yukarıdaki kod bloğunu a&ccedil;ıklarsak CLIENT_ID kısmına aldığımız id, aynı şekilde secret kısmına da diğer aldığımız kodu gireceğiz. Redirect_uri kısmı ise bu auth işlemini nerede ger&ccedil;ekleştirdiğinize bağlıdır. Ben şahsen bunu ayrı bir sayfada yapıp ardından access tokeni veritabanımda depolamayı tercih ediyorum. Bu kod bloğunu girdiğiniz sayfanın URL'sini yazmanız şimdilik yeterlidir.</p>
<p><strong>Refresh token kullanarak Access token yenilemek:</strong></p>
<pre>$session = new SpotifyWebAPI\Session('CLIENT_ID', 'CLIENT_SECRET', 'REDIRECT_URI');<br />$session-&gt;refreshAccessToken(Auth::user()-&gt;spotify_refresh_token);<br />/* bu iki değişkeni bir databasede saklamak cidden iyi fikir. */<br />$newAccessToken = $session-&gt;getAccessToken(); <br />$newRefreshToken = $session-&gt;getRefreshToken();</pre>
<p><br />Bu iki auth işlemini &ouml;ğrendikten sonra artık oyuna başlayabiliriz. Aşağıdaki iki kodla API nesnemizi oluşturalım:</p>
<pre>$api = new SpotifyWebAPI\SpotifyWebAPI(['auto_refresh' =&gt; true, 'auto_retry' =&gt; true]);
<span class="pl-s1"><span class="pl-c1">$</span>api</span>-&gt;<span class="pl-en">setAccessToken</span>(<span class="pl-s1"><span class="pl-c1">$</span>session</span>-&gt;<span class="pl-en">getAccessToken</span>());</pre>
<p>Ardından bu oluşturduğumuz $api değişkeni &uuml;zerinden işlemleri yapmaya başlayalım.</p>
<h3>addMyTracks - Sevilen Şarkılara Ekleme Yapmak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;addMyTracks</span>(<span class="pl-s1"><span class="pl-c1">$</span>tracks</span>)</pre>
</div>
<p>Access tokeni kullanılan kullanıcının k&uuml;t&uuml;phanesine par&ccedil;a ekler.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/library/save-tracks-user/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/library/save-tracks-user/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$tracks</code>&nbsp;<strong>string|array</strong> - Eklenecek par&ccedil;aların ID(leri) veya Spotify URI(ları)</li>
</ul>
<h4>Ne d&ouml;nd&uuml;r&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> Par&ccedil;a k&uuml;t&uuml;phaneye eklendi mi eklenmedi mi?</li>
</ul>
<p>&nbsp;</p>
<h3>getMyDevices - Kullanıcının Cihazlarını Almak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;getMyDevices</span>()</pre>
</div>
<p>Kullanıcının cihazları hakkında bilgileri almak.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/get-a-users-available-devices/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/get-a-users-available-devices/</a></p>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Kullanıcının cihazları. D&ouml;nen veri tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>changeMyDevice - &Ccedil;alan Cihazı Değiştirmek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;changeMyDevice</span>(<span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Kullanıcının m&uuml;zik &ccedil;aldığı cihazı değiştirir.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/transfer-a-users-playback/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/transfer-a-users-playback/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Gereken ayarların listesi.
<ul>
<li>string|array device_ids Zorunludur. Değiştirmek istediğiniz cihazın ID değerini girmelisiniz.</li>
<li>bool play Opsiyonel olarak diğer cihazda oynatmanın başlama durumunu belirlersiniz.</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;nd&uuml;r&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> İşlem başarılı mı?</li>
</ul>
<p>&nbsp;</p>
<h3>changeVolume - Sesi Değiştirmek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;changeVolume</span>(<span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Kullanıcının ses d&uuml;zeyini değiştirir.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/set-volume-for-users-playback/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/set-volume-for-users-playback/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Ses ayarları
<ul>
<li>int volume_percent Zorunlu, ayarlanacak sesin d&uuml;zeyi.</li>
<li>string device_id Opsiyonel, sesi değiştirilecek cihazın ID'si.</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> İşlem başarılı mı?</li>
</ul>
<p>&nbsp;</p>
<h3>createPlaylist - Playlist Oluşturmak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;createPlaylist</span>(<span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Yeni bir &ccedil;alma listesi oluşturun.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/playlists/create-playlist/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/playlists/create-playlist/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Playlist i&ccedil;in ayarlar listesi.
<ul>
<li>string name Zorunlu, &ccedil;alma listesinin ismi.</li>
<li>bool public Opsiyonel, &ccedil;alma listesi herkese a&ccedil;ık mı?</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Yeni &ccedil;alma listesinin kendisi, tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>me - Kullanıcının Bilgilerini Getirmek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;me</span>()</pre>
</div>
<p>O anda giriş yapmış olan kullanıcının bilgilerini alabilirsiniz.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/users-profile/get-current-users-profile/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/users-profile/get-current-users-profile/</a></p>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> O an giriş yapmış olan kullanıcının bilgileri d&ouml;ner. Tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>play - &Ccedil;almayı Başlatmak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-v">$api-&gt;</span><span class="pl-en">play</span>(<span class="pl-s1"><span class="pl-c1">$</span>deviceId</span>, <span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>&Ccedil;alma işlemini başlatır.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$deviceId</code>&nbsp;<strong>string</strong> - Opsiyonel, &ccedil;almak istediğiniz cihazın ID'si.</li>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Opsiyonel, ayarlar.
<ul>
<li>string context_uri Opsiyonel, &ccedil;alınacak şeyin Spotify URI'ı (farkı par&ccedil;a değil alb&uuml;m, podcast &ccedil;alabiliyor)</li>
<li>array uris Opsiyonel, &ccedil;alınacak par&ccedil;aların Spotify URI'ı.</li>
<li>object offset Opsiyonel, &ccedil;alma işleminin hangi kısımdan başlaması gerektiği.</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> İşlem başarılı mı?</li>
</ul>
<p>&nbsp;</p>
<h3>queue - &Ccedil;alma Sırasına Şarkı Eklemek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-v">$api-&gt;</span><span class="pl-en">queue</span>(<span class="pl-s1"><span class="pl-c1">$</span>trackUri</span>, <span class="pl-s1"><span class="pl-c1">$</span>deviceId</span>)</pre>
</div>
<p>&Ccedil;alma sırasına şarkı ekler.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/add-to-queue/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/add-to-queue/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$trackUri</code>&nbsp;<strong>string</strong> - Zorunlu, &ccedil;alacak par&ccedil;anın Spotify URI'ı.</li>
<li><code>$deviceId</code>&nbsp;<strong>string</strong> - Opsiyonel, cihaz ID'si.</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> İşlem başarılı mı?</li>
</ul>
<p>&nbsp;</p>
<h3>search - Arama Yapmak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-v">$api-&gt;</span><span class="pl-en">search</span>(<span class="pl-s1"><span class="pl-c1">$</span>query</span>, <span class="pl-s1"><span class="pl-c1">$</span>type</span>, <span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Spotify k&uuml;t&uuml;phanesinde arama yapmak i&ccedil;in kullanıyoruz.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/search/search/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/search/search/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$query</code>&nbsp;<strong>string</strong> - Aranacak terim.</li>
<li><code>$type</code>&nbsp;<strong>string|array</strong> - Aranacak terimin t&uuml;r&uuml;. album, artist, playlist, track, show, episode kelimelerinden birini veya birden fazlasını se&ccedil;ebilirsiniz.</li>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Arama ayarları
<ul>
<li>string market Opsiyonel, hangi pazarlarda aransın. &Uuml;lke kodu ile se&ccedil;ilir.</li>
<li>int limit Opsiyonel, ka&ccedil; sorgu d&ouml;ns&uuml;n?</li>
<li>int offset Opsiyonel, ka&ccedil; item ge&ccedil;ilsin?</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Arama sonu&ccedil;ları, tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>next - Sıradaki Par&ccedil;ayı &Ccedil;al</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-v">$api-&gt;</span><span class="pl-en">next</span>(<span class="pl-s1"><span class="pl-c1">$</span>deviceId</span>)</pre>
</div>
<p>Kullanıcının kuyruğundaki sıradaki par&ccedil;ayı &ccedil;alar.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/skip-users-playback-to-next-track/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/skip-users-playback-to-next-track/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$deviceId</code>&nbsp;<strong>string</strong> - Opsiyonel, &ccedil;alacak cihaz ID'si</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> İşlem başarılı mı?</li>
</ul>
<p>&nbsp;</p>
<h3>pause - Şarkıyı Durdurmak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;pause</span>(<span class="pl-s1"><span class="pl-c1">$</span>deviceId</span>)</pre>
</div>
<p>&Ccedil;alma işlemini durdurur.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/pause-a-users-playback/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/pause-a-users-playback/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$deviceId</code>&nbsp;<strong>string</strong> - Opsiyonel, &ccedil;alacak cihaz ID'si</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>boolean</strong> İşlem başarılı mı?</li>
</ul>
<p>&nbsp;</p>
<h3>getTrack - Şarkının Bilgilerini &Ccedil;ekmek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;getTrack</span>(<span class="pl-s1"><span class="pl-c1">$</span>trackId</span>)</pre>
</div>
<p>ID'sine sahip olduğunuz şarkının bilgilerini almak.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/tracks/get-track/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/tracks/get-track/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$trackId</code>&nbsp;<strong>string</strong> - ID veya Spotify URI girebilirsiniz, zorunludur.</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Şarkı bilgileri, t&uuml;r&uuml; return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>getMyCurrentPlaybackInfo - &Ccedil;alan Şarkıyı Hakkında Bilgi Almak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-v">$api-&gt;</span><span class="pl-en">getMyCurrentPlaybackInfo</span>()</pre>
</div>
<p>Kullanıcının o andaki oynatma bilgilerini almak.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/player/get-information-about-the-users-current-playback/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/player/get-information-about-the-users-current-playback/</a></p>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Kullanıcının oynatma bilgisi, d&ouml;nen veri tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>getMyPlaylists - Kullanıcının &Ccedil;alma Listelerini Almak</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;getMyPlaylists</span>(<span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Kullanıcının &ccedil;alma listelerini getirir.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/playlists/get-a-list-of-current-users-playlists/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/playlists/get-a-list-of-current-users-playlists/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Opsiyonel, ayarlar.
<ul>
<li>int limit Opsiyonel, ka&ccedil; tane getirilsin?</li>
<li>int offset Opsiyonel, ka&ccedil; tanesi ge&ccedil;ilsin?</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Kullanıcının playlistleri, d&ouml;nen veri tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>getMySavedTracks - Kullanıcının Beğendiği Şarkılar</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;getMySavedTracks</span>(<span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Kullanıcının beğendiği şarkıların listesi.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/library/get-users-saved-tracks/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/library/get-users-saved-tracks/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Opsiyonel, ayarlar.
<ul>
<li>int limit Opsiyonel, ka&ccedil; tane getirilsin?</li>
<li>int offset Opsiyonel, ka&ccedil; tanesi ge&ccedil;ilsin?</li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Kullanıcının beğendiği şarkılar, d&ouml;nen veri tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>getPlaylist - Playlist Hakkında Bilgi Getirmek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;getPlaylist</span>(<span class="pl-s1"><span class="pl-c1">$</span>playlistId</span>)</pre>
</div>
<p>IDsi belirli olan&nbsp;<br /><a href="https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlist/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlist/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$playlistId</code>&nbsp;<strong>string</strong> - Playlistin Spotify URI'ı.</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Playlist hakkında bilgiler, d&ouml;nen veri tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<h3>getPlaylistTracks - Playlistten Şarkıları &Ccedil;ekmek</h3>
<div class="highlight highlight-text-html-php">
<pre><span class="pl-en">$api-&gt;getPlaylistTracks</span>(<span class="pl-s1"><span class="pl-c1">$</span>playlistId</span>, <span class="pl-s1"><span class="pl-c1">$</span>options</span>)</pre>
</div>
<p>Playlistten şarkıları getirir.<br /><a href="https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlists-tracks/" rel="nofollow">https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlists-tracks/</a></p>
<h4>Arg&uuml;manlar</h4>
<ul>
<li><code>$playlistId</code>&nbsp;<strong>string</strong> - Playlistin Spotify URI'ı.<code></code></li>
<li><code>$options</code>&nbsp;<strong>array|object</strong> - Opsiyonel, ayarlar.
<ul>
<li>int limit Opsiyonel, ka&ccedil; tane getirilsin?</li>
<li>int offset Opsiyonel, ka&ccedil; tanesi ge&ccedil;ilsin?<code></code></li>
</ul>
</li>
</ul>
<h4>Ne d&ouml;n&uuml;yor?</h4>
<ul>
<li><strong>array|object</strong> Playlist hakkında bilgiler, d&ouml;nen veri tipini return_assoc ile değiştirebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<p>&Ouml;nemli olan t&uuml;m fonksiyonları &ccedil;evirdiğimi d&uuml;ş&uuml;n&uuml;yorum. Eksik kaldığını d&uuml;ş&uuml;nd&uuml;ğ&uuml;n&uuml;z kısımlar varsa yorum kısmında belirtirseniz sevinirim. Anlamadığınız kısımlarda da sorularınıza yanıt vermeye &ccedil;alışacağım. Okuduğunuz i&ccedil;in teşekk&uuml;r ederim. Umarım faydalı olmuştur.</p>
<p>&nbsp;</p>
<p>Faydalı kaynaklar:<br /><a href="https://github.com/jwilsson/spotify-web-api-php/tree/master/docs/method-reference">https://github.com/jwilsson/spotify-web-api-php/tree/master/docs/method-reference</a><br /><a href="https://github.com/jwilsson/spotify-web-api-php/tree/master/docs/examples">https://github.com/jwilsson/spotify-web-api-php/tree/master/docs/examples</a></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Laravel Passport ile API Authentication (Doğrulama)]]></title>
            <link>https://dogukan.dev/laravel-passport-ile-api-authentication-dogrulama</link>
            <guid>https://dogukan.dev/laravel-passport-ile-api-authentication-dogrulama</guid>
            <pubDate>Tue, 23 Jun 2020 21:17:09 GMT</pubDate>
            <description><![CDATA[
Yazdığınız API'ların bazı zamanlar güvensiz olduğunu, bu sistemde kullanıcı girişi olsa, ama nasıl yaparım diye düşündüğünüz olmuştur. Normal şartlarda bu işi JWT token sistemi implemente ederek yapabiliyoruz ancak bu siste...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5ef1cd643f136.png" alt="Laravel Passport API Authentication" border="0" /></p>
<p>Yazdığınız API'ların bazı zamanlar g&uuml;vensiz olduğunu, bu sistemde kullanıcı girişi olsa, ama nasıl yaparım diye d&uuml;ş&uuml;nd&uuml;ğ&uuml;n&uuml;z olmuştur. Normal şartlarda bu işi JWT token sistemi implemente ederek yapabiliyoruz ancak bu sistemi sıfırdan oluşturmak uzun zaman alıyor. Laravel hemen burada da imdadımıza yetişiyor :)</p>
<p>&nbsp;</p>
<h2>JWT Nedir?</h2>
<p>JWT'nin a&ccedil;ılımı JSON Web Token'dir. Bu tokeni kullanarak API sistemlerinizi kullanıcı bazlı ayrıştırabilir, g&uuml;venlik &ouml;nlemi olarak token sahibi olmayan kullanıcıların girişine izin vermeyebilirsiniz. &Ccedil;alışma mantığından bahsedecek olursak aşağıdaki resim &uuml;zerinden ilerleyelim.</p>
<p><img src="/images/1/5ef1cf8cdcad5.jpeg" alt="JWT Json Web Token" border="0" /></p>
<p>İlk &ouml;nce sisteme login olmak i&ccedil;in post atarsınız. G&ouml;nderdiğiniz veriler doğruysa server size tokeninizi d&ouml;nd&uuml;r&uuml;r. Ardından aldığınız tokeni diğer attığınız sorgularda Authorization (doğrulama) headerine koyar ve sorgu atarsanız client her seferinde tokeninizi kontrol eder ve doğruysa size yanıtı g&ouml;nderir. Sistem basit&ccedil;e bu şekilde işlemektedir ancak her clientin public ve secret keyleri vardır. JWT tokenlarının g&uuml;venliği buna g&ouml;re sağlanmaktadır. Bu sayede dışarıdan tokenlarınızı &uuml;retip sisteminize sorgu atamazlar.</p>
<p>&nbsp;</p>
<h1>Laravel Passport nasıl kurulur?</h1>
<p>Laravel projenizin hali hazırda oluşturulduğunu varsayarak anlatmaya devam edeceğim.&nbsp;</p>
<ul>
<li>İlk &ouml;nce Passport paketini kuralım:<br /><code>composer require laravel/passport</code><br /><br /></li>
<li>Ardından .env dosyanızı d&uuml;zenleyip <code>php artisan migrate</code> komutunu &ccedil;alıştırın.<br /><br /></li>
<li><code>php artisan passport:install</code> komutunu girelim ve keylerimiz oluşturulsun. Bu keyleri bir yere not alınız ihtiyacınız olacak.<br /><br /></li>
<li>Ardından giriş yapmak i&ccedil;in kullandığınız modelinizin (standart olarak app/User.php) i&ccedil;erisine <code>use Laravel\Passport\HasApiTokens;</code> satırını ekleyiniz.<br /><br /><img src="/images/1/5ef1d39cb5928.jpg" alt="" border="0" /><br /><br /></li>
<li>app/Providers/AuthServiceProvider.php i&ccedil;indeki boot metoduna <code>\Laravel\Passport\Passport::routes();</code> satırını ekleyiniz.<br /><br /><img src="/images/1/5ef1d3af78faf.jpg" alt="" border="0" /><br /><br /></li>
<li>config/auth.php dosyasında guards arrayi i&ccedil;erisindeki api =&gt; driver kısmını bulun ve driveri passport olarak değiştirin.<br /><br /><img src="/images/1/5ef1d3be9239c.jpg" alt="" border="0" /><br /><br /></li>
<li>Sistem kurulumumuz başarıyla tamamlanmıştır.<br /><br /></li>
</ul>
<h2>Laravel Passport Nasıl Kullanılır?</h2>
<ul>
<li>Sisteminizde bir kullanıcı a&ccedil;ın. Eğer nasıl yapacağınızı bilmiyorsanız <code>php artisan tinker</code> komutu ile oluşturabilirsiniz.<br /><code>App\User::create(['name' =&gt; 'Divergent', 'email' =&gt; 'me@dogukan.dev', 'password' =&gt; bcrypt('divergent')]);</code><br /><br /></li>
<li>Ardından aşağıdaki şekilde request bodynize form-datayı ekleyiniz.<br /><br /><img src="/images/1/5ef1d565f32dd.jpg" alt="" border="0" /><br /><br />Client id size verilen grant keyin idsidir.<br />Client secret size verilen grant keyin kendisidir.<br />Grant type nasıl giriş yapmak istediğinizi se&ccedil;tiğiniz alandır.<br />Username ve password da token almak istediğiniz kullanıcının giriş bilgileridir.<br /><br /></li>
<li>Bu şekilde istek attıktan sonra size d&ouml;necek olan tokeni Postman ile aşağıdaki şekilde kullanabilirsiniz. D&ouml;nen tokenin t&uuml;r&uuml; Bearer Token olacaktır.<br /><br /><img src="/images/1/5ef1d63de16a4.jpg" alt="" border="0" /><br /><br /><br /></li>
</ul>
<p>Okuduğunuz i&ccedil;in teşekk&uuml;r ederim. Sorularınızı yorum b&ouml;l&uuml;m&uuml;nde belirtirseniz d&ouml;n&uuml;ş yapıyorum. İyi &ccedil;alışmalar.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[WSL2 ile Windows Üzerinde Linux Kullanmak]]></title>
            <link>https://dogukan.dev/wsl2-ile-windows-uezerinde-linux-kullanmak</link>
            <guid>https://dogukan.dev/wsl2-ile-windows-uezerinde-linux-kullanmak</guid>
            <pubDate>Wed, 17 Jun 2020 23:00:15 GMT</pubDate>
            <description><![CDATA[
Bu makalede Windows'a Windows Subsystem for Linux 2'yi nasıl kuracağımızı öğreneceğiz. Öncelikle bahsetmemiz gereken konu tabiki de WSL2 nedir?
 
WSL Nedir?
WSL'in açılımı Windows Subsystem for Linux'tur. Basit olarak ko...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5ee9ff4f1d847.jpg" alt="Arch Linux WSL2" border="0" /></p>
<p><strong>Bu makalede Windows'a Windows Subsystem for Linux 2'yi nasıl kuracağımızı &ouml;ğreneceğiz. &Ouml;ncelikle bahsetmemiz gereken konu tabiki de WSL2 nedir?</strong></p>
<p>&nbsp;</p>
<h2>WSL Nedir?</h2>
<p>WSL'in a&ccedil;ılımı Windows Subsystem for Linux'tur. Basit olarak konuşursak sanal makine olmadan Linux terminalini Windows &uuml;zerinde kullanmanızı sağlamaktadır. Git Bash gibi bir ka&ccedil; UNIX komutu eklenmiş toollara g&ouml;re farklılık g&ouml;stermektedir. WSL &uuml;zerinde direkt olarak Linux uygulamalarını &ccedil;alıştırabilirsiniz. WSL Linux kernelini Windows &uuml;zerinde &ccedil;alıştırmak gibi bir şey. Kulağa iyi geliyor değil mi?</p>
<p>Eğer tecr&uuml;beli bir geliştiriciyseniz Linux'un sunucu bazlı işlemleri geliştirmek ve a&ccedil;ık kaynaklı teknolojileri kullanmak istediğinizde en iyi &ccedil;&ouml;z&uuml;m olduğunu bilirsiniz. Bu işlemlerin hepsini tabiki Windows &uuml;zerinde de yapabilirsiniz ancak kullanıcı deneyiminiz Linux &uuml;zerindeki kadar iyi olmayacaktır. Bulut hosting firmalarının bir &ccedil;oğu m&uuml;şterilerine stabilite i&ccedil;in Linux kullanımını &ouml;nermektedir. WSL kullanarak Linux gerektiren işlemlerinizi uyumluluk sorunları yaşamadan kolayca &ccedil;&ouml;zebilmektesiniz.</p>
<p>&nbsp;</p>
<h1>Arch Linux WSL Nasıl Kurulur?</h1>
<p>&Ouml;ncelikle Windows'un son s&uuml;r&uuml;m&uuml;ne sahip olmalısınız, eğer değilseniz de g&uuml;ncellemeniz gerekiyor. &Ccedil;alıştır penceresine winver yazarak s&uuml;r&uuml;m numarasını &ouml;ğrenebilirsiniz. Bu rehberi yazarken benim s&uuml;r&uuml;m numaram 2004/19041. Rehberdeki adımları takip ettiğinizde eğer hata aldıysanız Windows s&uuml;r&uuml;m&uuml;n&uuml;z&uuml; kontrol ediniz. S&uuml;r&uuml;m&uuml;n aynı olduğuna emin olduktan sonra Windows Subsystem for Linux'u etkinleştirmemiz gerekiyor.</p>
<p>Bu işlemi yapmak i&ccedil;in Denetim Masası -&gt; Programlar -&gt; Programlar ve &Ouml;zellikler penceresini takip ettiğinizde sol kısımda <strong>Windows &ouml;zelliklerini a&ccedil;</strong> d&uuml;ğmesine tıklıyoruz. Ardından aşağı kaydırıp Windows Subsystem for Linux se&ccedil;eneğini işaretleyin ve OK basın. Bilgisayarınızı yeniden başlatacak ve kurulumu ger&ccedil;ekleştirecektir.</p>
<p><img src="/images/1/5eea0215adbcb.png" alt="Activate WSL" border="0" /></p>
<p>&nbsp;</p>
<blockquote>
<p>Sisteminizde WSL'in &ccedil;alışması i&ccedil;in BIOS ayarlarınızdan UEFI'nin a&ccedil;ık olması gerekmektedir. Ayrıca sanallaştırma ayarının da a&ccedil;ık durumda bulunması zorunludur.</p>
</blockquote>
<p>&nbsp;</p>
<p>İkinci aşamamızda Windows i&ccedil;in Sanal Makine Platformunu aktifleştirmemiz gerekiyor. Bu işlemi yapabilmek i&ccedil;in &ouml;ncelikle <strong>y&ouml;netici olarak</strong> bir PowerShell penceresi &ccedil;alıştırın.&nbsp;</p>
<p><code>Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform</code> komutunu &ccedil;alıştırın ve bilgisayarınızı yeniden başlatın.</p>
<p>&nbsp;</p>
<p>&Uuml;&ccedil;&uuml;nc&uuml; aşamada Arch Linux WSL dağıtımını indiriniz. Github indirme sayfası <a href="https://github.com/yuk7/ArchWSL">https://github.com/yuk7/ArchWSL</a> bu adrestedir. Buradan zip dosyasını indirip kurulumu yapmak istediğiniz yere &ccedil;ıkartınız. Ardından Arch.exe dosyasını &ccedil;alıştırarak kurulumu ger&ccedil;ekleştirebilirsiniz. Eğer Arch değil de farklı bir dağıtım kurmak istiyorsanız bu aşamada Microsoft Store'a giriş yaparak Ubuntu, Kali gibi &ccedil;eşitli dağıtımları indirebilirsiniz.</p>
<p>Bu aşamadan sonra aslında WSL kurulumunu tamamlamış olacağız ancak Docker desteğini getiren WSL2'ye y&uuml;kseltme işlemini yapmamız gerekmekte. Yoksa tam verimli bir Linux performansı alamamaktayız.</p>
<p>WSL2 y&uuml;kseltmesini yapabilmek i&ccedil;in <a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi">https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi</a> adresinden WSL2 kernel g&uuml;ncellemesini indirip bilgisayarınıza kurun. Kurulum ger&ccedil;ekleştikten sonra PowerShelli y&ouml;netici olarak &ccedil;alıştırın ve <code>wsl --set-default-version 2</code> komutunu girin. Ardından Arch veya hangi distroyu kullanıyorsak y&uuml;kseltmek i&ccedil;in <code>wsl --set-version Arch 2</code> yazınız. Arch yerine distro adı gelecek ancak eğer isimden emin değilseniz <code>wsl -l -v</code> komutunu kullanarak y&uuml;kl&uuml; WSL distrolarını g&ouml;rebilirsiniz.</p>
<p>&nbsp;</p>
<p>Son aşamamızda kurulumun başarılı olduğunu g&ouml;rmek i&ccedil;in wsl -l -v komutunu girin. Aşağıdaki gibi &ccedil;ıktı alıyorsanız işlemimiz başarıyla tamamlanmış demektir.</p>
<p><img src="/images/1/5eea056403067.jpg" alt="Arch WSL version 2" border="0" /></p>
<p>&nbsp;</p>
<h3>WSL nasıl kullanılır?</h3>
<p>WSL'i kullanabilmek i&ccedil;in Microsoft Store &uuml;zerinden Windows Terminal kurulumu yapmalısınız. Ardından kapak fotoğrafında olduğu gibi kurulumunu yaptığınız distroyu başarılı şekilde kullanabileceksiniz.</p>
<p>&nbsp;</p>
<p>Makalemi okuduğunuz i&ccedil;in teşekk&uuml;r ederim, takıldığınız yerleri yorum b&ouml;l&uuml;m&uuml;nde sormaktan &ccedil;ekinmeyiniz.</p>
<p>&nbsp;</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[node.js Express ile RESTful API Geliştirmek]]></title>
            <link>https://dogukan.dev/nodejs-express-restful-api-gelistirmek</link>
            <guid>https://dogukan.dev/nodejs-express-restful-api-gelistirmek</guid>
            <pubDate>Fri, 10 Apr 2020 09:44:06 GMT</pubDate>
            <description><![CDATA[

  Bugün node.js ve
  Express kullanarak RESTful API’ları
  nasıl geliştirebileceğimizi temel olarak öğrenmeye çalışacağız.

 
Bilinmesi gereken kavramlar

  İlk önce API kavramı nedir bunu ele almamız gerekir. API kavramını...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5e8fa4f2b8f6c.jpg" alt="node.js" border="0" /></p>
<p>
  Bug&uuml;n <strong>n</strong><strong>ode.js</strong> ve
  <strong>Express</strong> kullanarak <strong>RESTful API</strong>&rsquo;ları
  nasıl geliştirebileceğimizi temel olarak &ouml;ğrenmeye &ccedil;alışacağız.
</p>
<p>&nbsp;</p>
<h3 class="western">Bilinmesi gereken kavramlar</h3>
<p>
  İlk &ouml;nce API kavramı nedir bunu ele almamız gerekir. API kavramının
  ingilizce olarak a&ccedil;ılımına bakarsak
  <strong>Application Programming Interface</strong> olarak ge&ccedil;er, yani
  Uygulama Programlama Aray&uuml;z&uuml; olarak &ccedil;evirebiliriz.
</p>
<p>
  API&rsquo;ları genelde program geliştiricileri birden fazla servisi bir arada
  kullanmak istediğinde kullanmaktadır. G&uuml;n&uuml;m&uuml;zdeki &ccedil;oğu
  sistem API kullanarak &ccedil;alışmaktadır. Bizim bug&uuml;n API geliştirme
  sebebimiz ise next.js ile geliştireceğimiz basit uygulamanın backend servisini
  yazmak olacak. API testimiz i&ccedil;in Postman uygulamasını kullanacağız.
  Altta linki mevcuttur.
</p>
<p>
  Kılavuza devam etmeden &ouml;nce
  <strong>JavaScript</strong> geliştirebildiğinize emin olun ve ES6
  yeniliklerinden l<strong>et, const ve arrow functions</strong> gibi
  yenilikleri &ouml;ğrenin.
</p>
<p>
  <strong>MongoDB</strong> &uuml;zerinde temel olarak bir bilginiz olması iyi
  olacaktır. Node.js &uuml;zerinde
  <strong>mongoose</strong> framework&uuml;n&uuml; kullanarak ilerleyeceğiz.
</p>
<p>
  Diğer bilmediğimiz kavram ise <strong>Express</strong>&rsquo;in ne olduğu
  olabilir diye d&uuml;ş&uuml;nd&uuml;ğ&uuml;mden onu da a&ccedil;ıklayalım.
  Express, <strong>node.js</strong> &uuml;zerinde en yaygın olarak kullanılan
  web geliştirme framework&uuml;d&uuml;r. Minimal ve hızlı olması, temiz kod
  prensiplerini benimsemesi Express&rsquo;i tercih edilebilir bir se&ccedil;enek
  haline getirmektedir. Bizim i&ccedil;in <strong>routing</strong> ve
  <strong>middleware</strong> işlemlerini &ccedil;&ouml;zmesi de g&uuml;zel
  taraflarındandır. Bu sebepten Express ile ilerleyeceğiz.
</p>
<p>
  Bu konular hakkında detaylı bilgi edinebileceğiniz kaynakları aşağıya
  bırakıyorum.
</p>
<p>&nbsp;</p>
<h3 class="western">Araştırma yapabileceğiniz kaynaklar ve linkler</h3>
<ul>
  <li>
    <p><a href="https://expressjs.com/">https://expressjs.com</a></p>
  </li>
  <li>
    <p><a href="https://nodejs.org/en/docs/">https://nodejs.org/en/docs/</a></p>
  </li>
  <li>
    <p>
      <a href="https://mongoosejs.com/docs/guide.html"
        >https://mongoosejs.com/docs/guide.html</a
      >
    </p>
  </li>
  <li>
    <p><a href="https://www.postman.com/">https://www.postman.com/</a></p>
  </li>
  <li>
    <p>
      <a href="https://github.com/dogukanoksuz/express-mongo-crud-app"
        >https://github.com/dogukanoksuz/express-mongo-crud-app</a
      >
    </p>
  </li>
</ul>
<h3 class="western">&nbsp;</h3>
<h3 class="western">
  node.js ve Express kurulumu, sisteminizde bulunması gerekenler
</h3>
<p>
  İlk &ouml;nce projemizi başlatabilmek i&ccedil;in sisteminizde node.js ve
  npm/yarn paket y&ouml;neticilerinden birinin kurulu olması gerekmektedir. Ben
  <strong>npm</strong> tercih ettiğimden kılavuzdaki komutları npm olarak
  kullanacağım. Sisteminize nasıl kurulacağını Google &uuml;zerinden
  araştırabilirsiniz.
  <em>(&ouml;rnek arama c&uuml;mleciği: windows nodejs kurulumu). </em>Bu eksiği
  kapattıktan sonra veritabanı y&ouml;netim sistemimiz olarak ben MongoDB
  kullanmak istiyorum. Siz tercihinize g&ouml;re diğer veritabanları i&ccedil;in
  <strong>ORM</strong>&rsquo;leri kullanabilirsiniz.
  <em
    >(<strong>MySQL, MariaDB</strong> i&ccedil;in
    <strong>Sequelize</strong> kullanılabilir.). </em
  >Sisteminize MongoDB kurulumu ger&ccedil;ekleştirirseniz kolayca kılavuzu
  takip edebilirsiniz. Linux &uuml;zerinde MongoDB i&ccedil;in
  <strong>devilbox</strong> kullanıyorum.
  <a
    title="Devilbox"
    href="/devilbox-ile-lamp-stack-kurulumu.html"
    target="_blank"
    rel="noopener"
    >Devilbox kurulumu ve kullanımını buraya tıklayarak takip edebilirsiniz.</a
  >
</p>
<p>&nbsp;</p>
<h2 class="western">RESTful API projesine başlangı&ccedil;</h2>
<p>
  Komutları Linux &uuml;zerinden anlatacağımı belirteyim ancak &ccedil;ok
  komplike olmadığından Windows ve macOS ile de eşleştirmeleri kolayca
  yapabilirsiniz.
</p>
<p>
  Bug&uuml;n temel olarak <strong>CRUD</strong> işlemlerini anlayabilmemiz
  i&ccedil;in tek modelli bir uygulama geliştireceğiz. Ben basit olarak notları
  tuttuğumuz bir uygulama se&ccedil;mek istedim. Siz isterseniz sadece yazılar
  modeline sahip bir uygulama geliştirip şemayı genişletebilirsiniz.
</p>
<p>&nbsp;</p>
<p>
  <strong>1.</strong> Terminalinizi a&ccedil;ın, uygulamanız i&ccedil;in bir
  klas&ouml;r oluşturun.
</p>
<p><code>$ mkdir node-express-app</code></p>
<p>&nbsp;</p>
<p>
  <strong>2.</strong> Klas&ouml;r&uuml;n&uuml;zde package.json dosyasını
  oluşturun.
</p>
<p><code>$ cd node-express-app</code></p>
<p><code>$ npm init</code></p>
<p>
  komutunu girdikten sonra &ouml;nemli olan tek kısım şu anlık entry point
  kısmı, diğerlerine enter basıp ge&ccedil;ebilirsiniz. Entry point
  se&ccedil;eneğine <code>server.js</code> yazınız.
</p>
<p>&nbsp;</p>
<p><strong>3.</strong> Express ve diğer paketlerin kurulumunu yapın.</p>
<p><code>$ npm install express body-parser mongoose &ndash;save</code></p>
<p>&nbsp;</p>
<p>
  <strong>4.</strong> Kurulum işlemi tamamlandıysa klas&ouml;r&uuml;n&uuml;zde
  <code>node_modules</code> klas&ouml;r&uuml; ve
  <code>package.json</code> dosyalarının oluşması gerekmektedir. Kontrol edin
  eğer problem yok gibi g&ouml;z&uuml;k&uuml;yorsa devam edebiliriz.
</p>
<p>&nbsp;</p>
<h3 class="western">Express web sunucusunun kurulumu</h3>
<p>
  Node.js geliştirebilmek i&ccedil;in gereken temel adımları halledip
  paketlerimizi kurduğumuza g&ouml;re klas&ouml;r&uuml;m&uuml;z&uuml; favori kod
  edit&ouml;r&uuml;m&uuml;zde a&ccedil;arak devam edelim. A&ccedil;tıktan sonra
  klas&ouml;r i&ccedil;erisine <code>server.js</code> adında bir dosya oluşturun
  ve i&ccedil;eriğini şu şekilde tanımlayın:
</p>
<p><gist id="3f45ad75e4b4ead7d860863c29847016"></gist></p>
<p>
  &Ouml;ncelikle <code>express</code> ve <code>body-parser</code> paket
  i&ccedil;eriye aktardık. Express&rsquo;in ne olduğundan zaten bahsettik.
  Body-parser ise &ccedil;eşitli i&ccedil;erik tiplerini Express &uuml;zerinde
  parse edip kullanmamızı sağlayan bir paket.
</p>
<p>
  Ardından express uygulamamızı oluşturduk. İki tane body-parser middlewareını
  <code>app.use()</code> metodunu kullanarak express uygulamamıza tanımladık. Bu
  tanımladığımız middlewarein request ve response objelerine erişimi var, bu
  objeler &uuml;zerinde &ccedil;eşitli işlemler ger&ccedil;ekleştirerek bizim
  kolayca işlemleri ger&ccedil;ekleştirmemizi sağlayacak.
</p>
<p>
  Sonrasında bir adet <code>GET</code> rotası i&ccedil;eren hoşgeldin mesajlı
  basic bir rota oluşturduk.
</p>
<p>
  En sonunda da 3000 portundan gelen istekleri dinleyeceğimiz web sunucumuzu
  başlattık.
</p>
<h5 class="western">&nbsp;</h5>
<h3 class="western">Veritabanı erişimini konfig&uuml;re etmek ve bağlanmak</h3>
<p>
  &Ouml;ncelikle config adında bir klas&ouml;r oluşturalım, veritabanı bağlantı
  c&uuml;mlemizi bu klas&ouml;re ekleyeceğiz. Ardından bu klas&ouml;r&uuml;n
  i&ccedil;inde db.config.js adında bir dosya oluşturun, i&ccedil;eriği şu
  şekilde olsun:
</p>
<p><code>module.exports = {</code></p>
<p>
  <code>&nbsp; &nbsp; url: 'mongodb://172.16.238.16:27017/backend-app'</code>
</p>
<p><code>}</code></p>
<p>
  Buradaki bağlantı c&uuml;mlesi sizin sunucu konfig&uuml;rasyonunuza g&ouml;re
  değişebilir ancak devilbox &uuml;zerinde bağlantıyı bu şekilde sağlamaktayız.
</p>
<p>
  Ardından aşağıdaki kod bloğunu <code>server.js</code> i&ccedil;erisinde
  <code>app.use(bodyParser.json())</code> altına ekleyin.
</p>
<p><gist id="95540bb7f193a6df2b0244e42a792b45"></gist></p>
<p>
  Bu işlemin ardından node server.js komutunu &ccedil;alıştırarak terminalden
  veritabanına bağlanabiliyor musunuz kontrol edin. G&ouml;rmeniz gereken mesaj
  &ldquo;Veritabanına bağlantı başarılı&rdquo; tarzı olmalıdır.
</p>
<p>&nbsp;</p>
<h3 class="western">Mongoose kullanarak model tanımlamak</h3>
<p>
  Veritabanı bağlantı, basit server işlemleri gibi şeyleri hallettiğimize
  g&ouml;re ilk modelimizi tanımlayabiliriz.
</p>
<p><code>$ mkdir -p app/models</code></p>
<p><code>$ cd app/models</code></p>
<p>
  komutlarını giriyoruz ve bu klas&ouml;r i&ccedil;erisinde
  <code>note.model.js</code> adında şu i&ccedil;eriğe sahip bir dosya
  oluşturuyoruz.
</p>
<p><gist id="1379143723e9da546d677674dfee6209"></gist></p>
<p>
  G&ouml;rd&uuml;ğ&uuml;n&uuml;z &uuml;zere not modelimiz gayet basit. Sadece
  başlık ve i&ccedil;erik olaraktan iki alan i&ccedil;ermekte. Timestamps ise
  otomatik olarak iki alan ekliyor ve g&uuml;ncelleme/oluşturulma tarihlerini
  tutuyor.
</p>
<p>&nbsp;</p>
<h3 class="western">Express kullanarak rotaları tanımlamak</h3>
<p>
  Sırada rotalarımızı tanımlama işlemimiz var.
  <code>app/routes</code> klas&ouml;r&uuml;n&uuml; oluşturalım.
</p>
<p><code>$ mkdir app/routes</code></p>
<p><code>$ cd app/routes</code></p>
<p>
  Şimdi <code>note.routes.js</code> adında bir dosya oluşturun ve i&ccedil;eriği
  şu olsun:
</p>
<p><gist id="83ec11bef1e249034440d7a78325c806"></gist></p>
<p>Ardından <code>app.listen()</code> satırından &ouml;nce şu kısmı ekleyin:</p>
<p><code>require(&lsquo;./app/routes/note.routes&rsquo;)(app)</code></p>
<p>
  Sunucuyu &ccedil;alıştırmaya &ccedil;alışırsanız başlamayacaktır
  &ccedil;&uuml;nk&uuml; rotalarımız i&ccedil;in controllerimizi hen&uuml;z
  tamamlamadık.
</p>
<p>&nbsp;</p>
<h3 class="western">Kontrolc&uuml; fonksiyonlarını oluşturmak</h3>
<p>
  <code>app/controllers</code> klas&ouml;r&uuml;n&uuml; oluşturun ve
  i&ccedil;erisine aşağıdaki i&ccedil;erikle
  <code>note.controller.js</code> dosyasını oluşturun.
</p>
<p><gist id="a4a11b0160ceef7dc9018d7c7f381580"></gist></p>
<p>
  Projemizde kullanılan Mongoose komutları i&ccedil;in&nbsp;API
  dok&uuml;mantasyonunu inceleyebilirsiniz.
</p>
<ul>
  <li>
    <p>
      <a
        href="http://mongoosejs.com/docs/api.html#document_Document-save"
        target="_blank"
        rel="noopener"
        >Mongoose save()</a
      >
    </p>
  </li>
  <li>
    <p>
      <a
        href="http://mongoosejs.com/docs/api.html#find_find"
        target="_blank"
        rel="noopener"
        >Mongoose find()</a
      >
    </p>
  </li>
  <li>
    <p>
      <a
        href="http://mongoosejs.com/docs/api.html#findbyid_findById"
        target="_blank"
        rel="noopener"
        >Mongoose findById()</a
      >
    </p>
  </li>
  <li>
    <p>
      <a
        href="http://mongoosejs.com/docs/api.html#findbyidandupdate_findByIdAndUpdate"
        target="_blank"
        rel="noopener"
        >Mongoose findByIdAndUpdate()</a
      >
    </p>
  </li>
  <li>
    <p>
      <a
        href="http://mongoosejs.com/docs/api.html#findbyidandremove_findByIdAndRemove"
        target="_blank"
        rel="noopener"
        >Mongoose findByIdAndRemove()</a
      >
    </p>
  </li>
</ul>
<h5 class="western">&nbsp;</h5>
<h4 class="western">Postman ile API test etmek</h4>
<p>Veritabanına eklenmiş t&uuml;m notları almak. (<code>GET /notes</code>)</p>
<p><img src="/images/1/5e8fa38813783.png" alt="GET notes" border="0" /></p>
<p>&nbsp;</p>
<p>Sadece bir not getirmek (<code>GET /notes/:noteId</code>)</p>
<p><img src="/images/1/5e8fa387e4b7b.png" alt="GET by Id" border="0" /></p>
<p>&nbsp;</p>
<p>Yeni bir not oluşturmak (<code>POST /notes</code>)</p>
<p><img src="/images/1/5e8fa387f3f6d.png" alt="POST notes" border="0" /></p>
<p>&nbsp;</p>
<p>Not silmek (<code>DELETE /notes/:noteId</code>)</p>
<p><img src="/images/1/5e8fa3879b23a.png" alt="Delete note" border="0" /></p>
<p>&nbsp;</p>
<h4 class="western">Sonu&ccedil;</h4>
<p>
  Bu yazımda temel olarak MongoDB, Express, Node stacki ile basit bir CRUD
  uygulaması nasıl geliştirebilirsiniz onu g&ouml;stermeye &ccedil;alıştım.
  Umarım faydalı olabilmişimdir. Sorularınızı yorum b&ouml;l&uuml;m&uuml;nde
  belirtirseniz elimden geldiğince yardımcı olmaya &ccedil;alışırım. Buraya
  kadar takip edenlere ayrıca teşekk&uuml;r ediyorum :)
</p>
<p>
  Projenin final hali:
  <a href="https://github.com/dogukanoksuz/express-mongo-crud-app"
    >https://github.com/dogukanoksuz/express-mongo-crud-app</a
  >
</p>
]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Arch Linux Kurulumu Videolu Anlatım]]></title>
            <link>https://dogukan.dev/arch-linux-kurulumu-videolu-anlatim</link>
            <guid>https://dogukan.dev/arch-linux-kurulumu-videolu-anlatim</guid>
            <pubDate>Fri, 27 Mar 2020 01:04:45 GMT</pubDate>
            <description><![CDATA[Arch Linux kurulumu aslında Linux'a yeni giriş yapacak çoğu kullanıcının korktuğu bir olay. İşin aslının böyle olmadığını göstermek için sitemizde olan Arch Linux Resimli Anlatımının bir de videolu olan versiyonunu kayda alal...]]></description>
            <content:encoded><![CDATA[<p><strong>Arch Linux kurulumu</strong> aslında <strong>Linux</strong>'a yeni giriş yapacak &ccedil;oğu kullanıcının korktuğu bir olay. İşin aslının b&ouml;yle olmadığını g&ouml;stermek i&ccedil;in sitemizde olan <a href="/arch-linux-kurulumu-resimli-anlatim.html" target="_blank" rel="noopener">Arch Linux Resimli Anlatımı</a>nın bir de videolu olan versiyonunu kayda alalım hem de o makaleyi yazdıktan sonra neler değişmiş onu g&ouml;reyim, makaleyi g&uuml;ncelleyeyim istedim. Sonu&ccedil; olarak yavaş internet hızıma rağmen 1 saat 20 dakika i&ccedil;erisinde <strong>Arch Linux</strong> kurulumunu ger&ccedil;ekleştirdim. Sanırım indirmeleri beklediğim kısımlar dışında 20 dakika uğraştım. Resimli anlatımıma kıyasla bu sefer EFI olarak kurulum ger&ccedil;ekleştirdim. Windows ile dualboot yapabilmenin inceliklerini de anlatmaya &ccedil;alıştım. Bonus olarak <a href="/arch-linux-kde-plasma-kurulumu.html" target="_blank" rel="noopener">Arch Linux KDE Plasma Kurulumu</a>nu da ger&ccedil;ekleştirdik. Umarım faydalı bir video olmuştur.</p>
<center><iframe src="https://www.youtube-nocookie.com/embed/7V3qUZ35ZtI" width="560" height="315" frameborder="0"></iframe></center>
<p>Eğer takıldığınız kısımlar olursa veya soru sormak isterseniz yorum b&ouml;l&uuml;m&uuml;n&uuml; kontrol ediyorum. Yorumlarınızı ve eleştirilerinizi bekliyorum.</p>
<p style="text-align: center;"><strong>İnsanlık i&ccedil;in faydalı bir şeyler yapabiliyorsam ne mutlu bana.</strong></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Arch Deepin Wi-Fi Bağlantı Problemi]]></title>
            <link>https://dogukan.dev/arch-deepin-wi-fi-baglanti-problemi</link>
            <guid>https://dogukan.dev/arch-deepin-wi-fi-baglanti-problemi</guid>
            <pubDate>Wed, 18 Mar 2020 02:07:24 GMT</pubDate>
            <description><![CDATA[
Merhaba dostlar, elimden geldiğinde uzun araştırmalar sonucunda bulduğum fixleri yayınlamaya çalışıyorum. Bugün de Deepin kurduktan sonra Wi-Fi bağlantı problemi yaşadım. Bunu çözmek için github üzerinde gezinirken bir fix ...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5e7149c39cf31.png" alt="Arch Deepin" border="0" data-featherlight="/images/1/5e7149c39cf31.png" /></p>
<p>Merhaba dostlar, elimden geldiğinde uzun araştırmalar sonucunda bulduğum fixleri yayınlamaya &ccedil;alışıyorum. Bug&uuml;n de Deepin kurduktan sonra Wi-Fi bağlantı problemi yaşadım. Bunu &ccedil;&ouml;zmek i&ccedil;in github &uuml;zerinde gezinirken bir fix buldum. Araştırması kolay olmadığımdan burada da yayınlamaya karar verdim.</p>
<h3>Problem:</h3>
<p>Wi-Fi bağlantısına tıklanıp şifre yazılıyor ancak bağlantı sağlanamıyor. Wi-Fi s&uuml;rekli kopmuş ve yeniden bağlanmaya &ccedil;alışıyormuş gibi oluyor.</p>
<p>&nbsp;</p>
<h2>Bağlantı Probleminin &Ccedil;&ouml;z&uuml;m&uuml;:</h2>
<ul>
<li><code>su</code> yazarak root olun.</li>
<li><code>systemctl disable dhcpcd</code> komutunu girin. NetworkManager ile &ccedil;akıştığından problem yaratabilir.</li>
<li><code>nano /etc/NetworkManager/NetworkManager.conf</code> komutunu girerek belirtilen dosyayı a&ccedil;ın.</li>
<li>Şu satırları ekleyin:<br /><code>[device]</code><br /><code>wifi.scan-rand-mac-address=no</code></li>
<li><code>reboot</code> yazarak sistemi yeniden başlatın.</li>
<li>Tekrar giriş yaptığınızda başarılı şekilde Wi-Fi bağlantısı yapabiliyor olmalısınız.</li>
</ul>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Wordpress Temasına Admin Paneli Eklemek]]></title>
            <link>https://dogukan.dev/wordpress-temasina-admin-paneli-eklemek</link>
            <guid>https://dogukan.dev/wordpress-temasina-admin-paneli-eklemek</guid>
            <pubDate>Fri, 06 Mar 2020 21:38:44 GMT</pubDate>
            <description><![CDATA[
  


  Bugün wordpress temanıza nasıl
  admin paneli (yönetim paneli) ekleyebileceğinizi
  anlatacağım. Kendi yazdığınız temaya kullanımı kolay olsun diye veya
  satılabilsin diye admin paneli eklemek isteyebilirsiniz. Bunu ...]]></description>
            <content:encoded><![CDATA[<p>
  <img
    src="/images/1/5e627c0cd9753.jpg"
    alt="Codestar Framework"
    border="0"
    data-featherlight="/images/1/5e627c0cd9753.jpg"
  />
</p>
<p>
  Bug&uuml;n <strong>wordpress temanıza</strong> nasıl
  <strong>admin paneli</strong> (y&ouml;netim paneli) ekleyebileceğinizi
  anlatacağım. Kendi yazdığınız temaya kullanımı kolay olsun diye veya
  satılabilsin diye admin paneli eklemek isteyebilirsiniz. Bunu yapmanın
  &ccedil;ok kolay bir yolu var. Tek ihtiyacınız olan PHP syntaxını bilmek ve
  biraz ingilizce okuyabilmek.
</p>
<p>
  Makalemizde <strong>Codestar Framework</strong>'tan bahsedeceğiz.
  &Ouml;ncelikle
  <a href="https://codestarframework.com/">https://codestarframework.com/</a>
  adresini ziyaret ediyoruz ve gerekli dosyaları "Download Free Version"
  d&uuml;ğmesine basarak indiriyoruz. İndirdiğimiz dosyaları bir yere
  &ccedil;ıkartıyoruz. Ardından temamıza nasıl kuracağımıza ge&ccedil;elim.
</p>
<h2>Codestar Framework admin paneli nasıl kurulur?</h2>
<ul>
  <li>
    &Ccedil;ıkardığımız dosyaları temanın istediğimiz bir dizinine koyabiliriz.
    Ben '/inc' adında bir klas&ouml;r a&ccedil;ıp onun altına atmayı tercih
    ediyorum.
  </li>
  <li>
    Ardından <em><strong>functions.php</strong></em> dosyamızı a&ccedil;ıyoruz.
    İ&ccedil;erisine
    <code
      >require get_template_directory() .
      '/inc/framework/codestar-framework.php';</code
    >
    şeklinde framework&uuml;m&uuml;z&uuml;n bootstrapper dosyasını ekliyoruz.
    Dosya yolu sizin se&ccedil;tiğiniz isme g&ouml;re değişiklik
    g&ouml;sterebilir. Ezbere bakmayınız.
  </li>
  <li>Admin panelimizin kurulumunu başarıyla tamamladık :)</li>
</ul>
<p><gist id="84054ddf06dff56b5cab606cd947e306"></gist></p>
<h3>Codestar Framework admin paneli nasıl kullanılır?</h3>
<p>
  Kurulumu tamamladık ama admin panelini nasıl kullanacağız ne yapacağız
  dediğinizi duyar gibiyim. Yukarıdaki kod bloğunu incelersek bu bir admin
  panelini nasıl tanımladığımızı g&ouml;steriyor. Adım adım incelemeye
  başlayalım.
</p>
<ul>
  <li>
    &Ouml;ncelikle bir php dosyası a&ccedil;ın ve bunu functions.php
    i&ccedil;erisinde Codestar framework &ccedil;ağırdığımız yerin hemen altında
    &ccedil;ağırın. &Ccedil;ağırmak i&ccedil;in require komutunu
    kullanabilirsiniz.
  </li>
  <li>
    <code>if ( class_exists( 'CSF' ) )</code> satırı framework&uuml;n doğru
    şekilde &ccedil;ağırıldığını kontrol ettiğimiz kısım. Framework import
    edilmediyse hata almamızı &ouml;nlemek i&ccedil;in kullandığımız bir satır.
  </li>
  <li>
    <code>$prefix</code> diye tanımladığımız kısım temanın kısa adını
    belirttiğimiz b&ouml;l&uuml;m. Diğer bildiğiniz temalarla ortak olmaması ve
    bir kez belirledikten sonra değiştirmemeniz iyi olacaktır. Tema ayarları bu
    kısa isimle veritabanında saklanacaktır.
  </li>
  <li>
    <code>CSF::createOptions</code> fonksiyonu i&ccedil;erisinde admin panelinin
    genel &ouml;zelliklerini tanımlıyoruz. Ben en gerekli &ouml;zellikleri
    yukarıdaki kısımda belirttim. &Ouml;rneğin framework_title keyi admin
    paneline girdiğimizde headerde g&ouml;z&uuml;kecek ismi belirtmektedir. Menu
    settings diye belirttiğim kısımda ise admin panelinin men&uuml;s&uuml;nde
    nasıl g&ouml;z&uuml;keceğini belirlemekteyiz.&nbsp;
  </li>
  <li>
    <code>CSF::createSection</code> fonksiyonu ile admin paneli i&ccedil;erisine
    alt sayfalar a&ccedil;maktayız. Ben bu &ouml;rnekte tek bir alt sayfa
    oluşturarak onun &uuml;zerinden ilerlemeyi tercih ediyorum.
  </li>
  <li>Title kısmında section yani alt sayfanın başlığını belirtiyoruz.</li>
  <li>
    Fields keyinin value kısmındaki array altına alt arrayler oluşturarak
    elemanları ekleme işlemini yapıyoruz. Bir &ouml;rnek vermemiz gerekirse tema
    rengi se&ccedil;eneği diye belirttiğim kısımda radio button kullanarak
    se&ccedil;enek almışım. Buradaki en &ouml;nemli kısım
    <em><strong>id</strong></em> kısmıdır. Buraya verdiğiniz isimler kolay ve
    aklınızda kalıcı isimler olsun. Sonradan temanın i&ccedil;erisinde bunları
    kullanacaksınız.&nbsp;
  </li>
  <li>
    Yukarıdaki kodda &ccedil;eşitli bi&ccedil;imlerin kullanımına &ouml;rnekler
    yazılmıştır. Diğer elementleri de incelemek isterseniz:
    <a href="https://codestarframework.com/documentation/#/fields"
      >https://codestarframework.com/documentation/#/fields</a
    >
    adresini ziyaret ederek &ouml;rnek kod kullanımları ile elementlerin hepsini
    g&ouml;rebilirsiniz.
  </li>
</ul>
<p>&nbsp;</p>
<h3>Bu ayarları temanın i&ccedil;inde nasıl &ccedil;ağıracağım?</h3>
<ul>
  <li>
    Ayarları &ccedil;ağırmak istediğiniz yerde
    <code>$options = get_option( 'Belirlediğiniz prefix' );</code> kodunu
    kullanarak <code>$options</code> &uuml;zerine ayarların &ccedil;ağırıldığı
    bir array eklersiniz. O arrayin i&ccedil;eriğini
    <code>var_dump($options);</code> aracılığı ile g&ouml;r&uuml;nt&uuml;leyip
    kolayca erişim sağlayabilirsiniz.
  </li>
  <li>
    &Ouml;rneğin yukarıda anlattığımız theme-color yani tema rengi
    se&ccedil;eneğini &ccedil;ağırıp kullanalım.
  </li>
  <li>
    <pre>if ( 'dark' == $options['theme-color'] ) {&nbsp;<br />   wp_enqueue_style( 'divergent-bootstrap', get_template_directory_uri() . '/dist/css/bootstrap-dark.min.css' );<br />&nbsp; &nbsp;wp_enqueue_style( 'divergent-style', get_template_directory_uri() . '/dist/css/style-dark.css' );<br />} elseif ( 'grey' == $options['theme-color'] ) {<br />  &nbsp;wp_enqueue_style( 'divergent-bootstrap', get_template_directory_uri() . '/dist/css/bootstrap-blue.min.css' );&nbsp; &nbsp;<br />   wp_enqueue_style( 'divergent-style', get_template_directory_uri() . '/dist/css/style-blue.css' );<br />}</pre>
  </li>
  <li>
    Yukarıda g&ouml;rebileceğiniz gibi $options değişkeninin ['theme-color']
    keyine ulaşarak o se&ccedil;eneği almışım ve kontrol&uuml;n&uuml; yaparak
    farklı stil dosyalarının &ccedil;ağırılmasını sağlamışım. Bunun gibi daha
    nice &ouml;rnek verebiliriz. Detaylı kullanım g&ouml;rmek istiyorsanız
    sitemde yayınladığım
    <a
      title="divergent 100% SEO Uyumlu Wordpress Blog Teması"
      href="/divergent-seo-uyumlu-wordpress-blog-temasi.html"
      target="_blank"
      rel="noopener"
      >divergent 100% SEO Uyumlu Wordpress Blog Teması</a
    >
    makalemdeki temanın dosyalarını inceleyerek fikir alabilirsiniz.
  </li>
</ul>
<p>&nbsp;</p>
<h3>&Ouml;rnek admin paneli g&ouml;r&uuml;nt&uuml;s&uuml;</h3>
<p>
  <img
    src="/images/1/5e5bc8d664305.png"
    alt="Codestar framework"
    border="0"
    data-featherlight="/images/1/5e5bc8d664305.png"
  />
</p>
<p>
  Makalemi okuyup zaman ayırdığınız i&ccedil;in teşekk&uuml;r ederim.
  Sorularınız i&ccedil;in aşağıdaki yorum b&ouml;l&uuml;m&uuml;n&uuml;
  kullanmaktan &ccedil;ekinmeyiniz, yardımcı olacağım.&nbsp;
</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[divergent 100% SEO Uyumlu Wordpress Blog Teması]]></title>
            <link>https://dogukan.dev/divergent-seo-uyumlu-wordpress-blog-temasi</link>
            <guid>https://dogukan.dev/divergent-seo-uyumlu-wordpress-blog-temasi</guid>
            <pubDate>Sun, 01 Mar 2020 19:40:53 GMT</pubDate>
            <description><![CDATA[
Merhaba dostlar,
Uzun zamandır yazı giremiyordum. Okulun güz dönemi bitince biraz gevşemek ve işlere ara vermek istedim açıkçası. Sonra da bir abimizin ricası ile websitemdeki temayı Wordpress'e entegre etmeye karar verdim...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5e5bc612a3ead.png" alt="divergent Wordpress Teması" border="0" data-featherlight="/images/1/5e5bc612a3ead.png" /></p>
<p>Merhaba dostlar,</p>
<p>Uzun zamandır yazı giremiyordum. Okulun g&uuml;z d&ouml;nemi bitince biraz gevşemek ve işlere ara vermek istedim a&ccedil;ık&ccedil;ası. Sonra da bir abimizin ricası ile websitemdeki temayı Wordpress'e entegre etmeye karar verdim.&nbsp;</p>
<p>Zaten websitemde halihazırda kullandığım ve 100% SEO uyumlu olan kendi yazdığım temamı modern standartlara uygun olarak ve yeni &ouml;zellikler ekleyerek entegrasyonunu tamamladım.</p>
<h2>Temanın &ouml;zellikleri</h2>
<ul>
<li>100% SEO uyumlu</li>
<li>Microcode, Schema.org entegrasyonları yapılı</li>
<li>Tema paneli var</li>
<li>İki renk se&ccedil;eneği var (siyah, beyaz)</li>
<li>Modern g&ouml;r&uuml;n&uuml;m</li>
<li>Temiz kodlama standartlarına uygunluk</li>
<li>HTML5 article/aside gibi tagların doğru şekilde kullanımı</li>
<li>Codestar framework ve _s boilerplate kullanıldı</li>
<li>Thumbnail boyutları her zaman otomatik ve d&uuml;zg&uuml;n şekilde ayarlanıyor</li>
<li>Men&uuml;ler Wordpress paneli &uuml;zerinden ayarlanabiliyor</li>
<li>Eklentilerden bağımsız, hi&ccedil; eklenti ihtiyacınız yok</li>
</ul>
<p>&nbsp;</p>
<h3>Temanın diğer renk şeması</h3>
<p><img src="/images/1/5e5bc7839533b.png" alt="divergent Wordpress Teması Beyaz Renk" border="0" data-featherlight="/images/1/5e5bc7839533b.png" /></p>
<p>Bu renk şemasını temayı entegre etmemi isteyen abimin isteği &uuml;zerine yaptım. Siyah ve kırmızı tonları yerine beyaz, mavi ve gri tonlarını kullanarak daha g&uuml;ven vaat eden bir renk şeması se&ccedil;tik. Tema paneli &uuml;zerinden zevkinize g&ouml;re iki se&ccedil;eneği de kolayca tercih edebiliyorsunuz.</p>
<p>&nbsp;</p>
<h3>Temanın y&ouml;netim paneli</h3>
<p><img src="/images/1/5e5bc8d664305.png" alt="Codestar Framework" border="0" data-featherlight="/images/1/5e5bc8d664305.png" /></p>
<h3>&nbsp;</h3>
<h3>Temada problem var ne yapacağım?</h3>
<p>Github &uuml;zerinden temanın issuelarını s&uuml;rekli kontrol edip yeni bugfixler yapacağım, bunları da release olarak tekrardan github'da yayınlayacağım. Changelogları bu sayfa &uuml;zerinden tekrar paylaşırım, indirme sayfasında her zaman en yeni s&uuml;r&uuml;m&uuml; g&ouml;receksiniz.</p>
<p>&nbsp;</p>
<h3>Temanın kodlarını kullanabilir miyim?</h3>
<p>Bu temayı tamamen &uuml;cretsiz olarak dağıtıyorum ve kodlarını istediğiniz yerde istediğiniz şekilde kullanabilirsiniz.</p>
<p>&nbsp;</p>
<h3>Temanın indirme linki</h3>
<p>divergent Wordpress Temasını buraya tıklayarak indirebilirsiniz:<br /><a title="divergent Wordpress Teması İndir" href="https://github.com/dogukanoksuz/divergent-wordpress-theme/releases" target="_blank" rel="noopener">https://github.com/dogukanoksuz/divergent-wordpress-theme/releases</a></p>
<p><em>*footer linkini kaldırmazsanız &ccedil;ok sevinirim :)</em></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[PHP Instagram API Fotoğraf Video Yükleme İşlemleri]]></title>
            <link>https://dogukan.dev/php-instagram-api-fotograf-video-yuekleme-islemleri</link>
            <guid>https://dogukan.dev/php-instagram-api-fotograf-video-yuekleme-islemleri</guid>
            <pubDate>Fri, 10 Jan 2020 23:12:53 GMT</pubDate>
            <description><![CDATA[

  Instagram API üzerine yaptığım paylaşımların rağbet görmesi sebebi
  ile devam gönderisi olarak bu yazıyı yayınlıyorum. Geçmiş yazı
  olan
  Instagram API ile Story İşlemleri
  yazımı okumanızı tavsiye etmekteyim. O yazıd...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d9b2eadc187b.jpg" alt="Instagram" border="0" /></p>
<p>
  Instagram API &uuml;zerine yaptığım paylaşımların rağbet g&ouml;rmesi sebebi
  ile devam g&ouml;nderisi olarak bu yazıyı yayınlıyorum. Ge&ccedil;miş yazı
  olan
  <a
    title="Instagram API ile Story İşlemleri"
    href="/php-instagram-api-ile-story-islemleri.html"
    target="_blank"
    rel="noopener"
    >Instagram API ile Story İşlemleri</a
  >
  yazımı okumanızı tavsiye etmekteyim. O yazıda temel olarak Instagram API'ı
  nasıl kullanacağınızı anlatan i&ccedil;erikler de mevcut.
</p>
<p>
  &Ouml;ncelikle Instagram API'ı kurmamız gerekmekte. İhtiyacınız olan şey
  sisteminize&nbsp;<a
    title="php"
    href="https://php.net/"
    target="_blank"
    rel="noopener"
    >PHP 7.3.9</a
  >&nbsp;ve&nbsp;<a
    title="composer"
    href="https://getcomposer.org/"
    target="_blank"
    rel="noopener"
    >Composer</a
  >&nbsp;kurulumlarını ger&ccedil;ekleştirmek. Bu işlemleri yaptıktan sonra yeni
  projemizi oluşturmaya başlayabiliriz.
</p>
<p>
  Projemizin i&ccedil;inde bulunacağı klas&ouml;r&uuml; oluşturun (Linux
  i&ccedil;in&nbsp;<code>mkdir &lt;projeAdı&gt;</code>&nbsp;komutunu
  kullanabiliriz). Ardından terminal, konsol veyahut ne kullanıyorsanız o
  uygulamayı a&ccedil;ın ve proje dizinine girdikten sonra (<code>cd </code
  >),&nbsp;<code>composer require mgp25/instagram-php</code>&nbsp;komutu ile
  Instagram API dosyalarını projemize dahil edelim.
</p>
<p>
  Bu işlemleri tamamladıktan sonra artık kodlarımızı yazacağımız dosyamızı
  oluşturabiliriz. Kendinize bir php dosyası a&ccedil;ın
  (&ouml;r:&nbsp;<code>instagram-islemleri.php</code>). Dosyayı oluşturduktan
  sonra php taglarını a&ccedil;ın ve yapmamız gereken bir ka&ccedil; rutin işlem
  mevcut.
</p>
<p>
  Composer aracılığı ile eklediğimiz kodların &ccedil;alışması i&ccedil;in
  ise&nbsp;<code>require 'vendor/autoload.php';</code>&nbsp;komutunu girmemiz
  yeterli, bu satırı ekledikten sonra composer ile eklediğiniz t&uuml;m paketler
  sorunsuzca projenizde &ccedil;alışmaya başlayacaktır.
</p>
<p>
  Başlamak i&ccedil;in gereken t&uuml;m işlemleri hallettiğimize g&ouml;re işe
  koyulabiliriz. &Ouml;ncelikle API unofficial yani gayriresmi olduğundan yazarı
  bizim riskleri kabul etmemizi istemiş. API classındaki kodların
  &ccedil;alışabilmesi i&ccedil;in&nbsp;<code
    ><span class="pl-c1">\InstagramAPI\</span
    ><span class="pl-c1">Instagram</span><span class="pl-k">::</span
    ><span class="pl-smi">$allowDangerousWebUsageAtMyOwnRisk</span>&nbsp;<span
      class="pl-k"
      >=</span
    >&nbsp;<span class="pl-c1">true</span>;</code
  >&nbsp;satırını kodumuza ekliyoruz. Artık API işlemlerini sorunsuzca
  ger&ccedil;ekleştirmeye başlayabiliriz.
</p>
<p>
  Classımızdan yeni bir obje &uuml;retmek i&ccedil;in&nbsp;<code
    >$ig = new \InstagramAPI\Instagram(true, true);</code
  >&nbsp;kodunu kullanıyoruz. Arg&uuml;manların anlamlarını a&ccedil;ıklamak
  gerekirse birinci arg&uuml;man debug aktif ediyor ikinci arg&uuml;man ise
  debugdaki gereksiz t&uuml;m kod par&ccedil;acıklarını kısaltıyor
  (truncate).&nbsp;
</p>
<p>
  Giriş yapmak i&ccedil;in bir try, catch bloğu tanımlayın. Catch bloğunun
  arg&uuml;manı&nbsp;<code>\Exception $e</code>&nbsp;olursa kolayca hata
  yakalama işlemlerini ger&ccedil;ekleştirebiliriz. try bloğunun
  i&ccedil;erisine&nbsp;<code>$ig-&gt;login($username, $password);</code
  >&nbsp;komutunu yazdığınızda API'a login olacaksınız.&nbsp;
</p>
<p>Bu işlemlerin sonucunda hata almıyorsanız işlemlere devam edebiliriz.</p>
<h2>Instagram API ile Fotoğraf, Video ve Alb&uuml;m Y&uuml;kleme İşlemleri</h2>
<p><gist id="6ecdba961c5962f656a535e48bd6b4f5"></gist></p>
<p>
  Yukarıdaki kod bloğunda t&uuml;m işlemler anlaşılır bir bi&ccedil;imde
  kodlanmıştır. Anlamadığınız kısımları yorum b&ouml;l&uuml;m&uuml;nde
  belirtmekten &ccedil;ekinmeyiniz. İyi &ccedil;alışmalar dilerim :)
</p>
]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Laravel Eloquent One to Many İlişkisi]]></title>
            <link>https://dogukan.dev/laravel-eloquent-one-to-many-iliskisi</link>
            <guid>https://dogukan.dev/laravel-eloquent-one-to-many-iliskisi</guid>
            <pubDate>Wed, 20 Nov 2019 23:35:25 GMT</pubDate>
            <description><![CDATA[
Bugün Laravel'de one to many ilişkisi detaylı olarak nasıl kurulur bundan bahsedeceğim.
 
One to Many ilişkisi nedir?

One to Many ilişkisi bir varlığın bir şeye fazla sayıda sahip olabileceğini ancak o şeyin sadece tek...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5dd580fc3a310.jpg" alt="Laravel 6" border="0" /></p>
<p>Bug&uuml;n <strong>Laravel</strong>'de one to many ilişkisi detaylı olarak nasıl kurulur bundan bahsedeceğim.</p>
<p>&nbsp;</p>
<h2>One to Many ilişkisi nedir?</h2>
<p><img src="/images/1/5dd5840072aa6.png" alt="One to Many" border="0" /></p>
<p><strong>One to Many</strong> ilişkisi bir varlığın bir şeye fazla sayıda sahip olabileceğini ancak o şeyin sadece tek kişiye ait olabileceğini g&ouml;steren ilişki t&uuml;r&uuml;d&uuml;r. &Ouml;rnek vermemiz gerekirse kullanıcı ve yazıları ele alalım. Bir yazının birden fazla yazarı yani kullanıcısı olamazken bir kullanıcı sonsuz sayıda yazı paylaşabilir. One to Many ilişkisi bu mantıkla ilerleyen t&uuml;m ilişkilerde kullanacağımız t&uuml;rd&uuml;r. Bu ilişkiyi tanımlayıp kullanabilmemiz i&ccedil;in sonsuz sayıda sahip olunabilinen girdi &uuml;zerinde kullanıcıya dair unique(tekil) bir değer teşkil eden s&uuml;tunu foreign key(yabancı anahtar) olarak g&ouml;stermemiz gerekmektedir.</p>
<p>&nbsp;</p>
<h2>One to Many ilişkisi nasıl kurulur?</h2>
<ul>
<li>&Ouml;ncelikle <strong>model</strong>lerinizi tanımlamanız gerekmektedir. Ben anlatımımda kullanıcı ve yazılar arasındaki ilişkiyi &ouml;rnek g&ouml;stererek ilerleyeceğim. Kullanıcı ve yazılar adında iki model a&ccedil;ıp bunların <strong>fillable, guarded</strong> alanlarını tanımlayınız.<br /><br /></li>
<li>Veritabanı <strong>migration</strong>larımızı oluşturalım. Kullanıcı migrationumuzda ekstra bir işlem yapmamıza gerek yok. Yazılar migrationumuzda işlemlerimizi yapmaya başlayalım.<br /><code>$table-&gt;unsignedBigInteger('user_id');</code> komutu ile foreign keyimizin bulunacağı s&uuml;tunu a&ccedil;ıyoruz. Burada model ismine g&ouml;re oluşturmanızda fayda bulunmaktadır.<br /><br /></li>
<li>Yazılar migrationumuzda <strong>foreign key</strong> s&uuml;tunumuzu tanımladıktan sonra <code>$table-&gt;foreign('user_id')-&gt;references('id')-&gt;on('users');</code> kod bloğunu kullanarak foreign keyimizin &ouml;zelliklerini tanımlıyoruz. <br /><br /></li>
<li>Daha detaylı yaklaşmak gerekirse <code>$table-&gt;foreign('key_ismi')</code> foreign keyimizin adını g&ouml;stermektedir. <code>-&gt;references('id')</code> kısmı ise ilişkiyi kuracağımız diğer tablodaki hangi s&uuml;tunu işaret ettiğimizi belirliyor. <code>-&gt;on('tablo_ismi')</code> kısmı ise hangi tabloya bakması gerektiğini g&ouml;steriyor.<br /><br /></li>
<li>Migration &uuml;zerinde <strong>foreign key</strong> ilişkilerini tamamladıktan sonra kullanıcı modeli yani tekil olan modele geliyoruz. Alttaki kod bloğu ile ilişkiyi tamamlıyoruz:<br /><code>public function post()</code><br /><code>{</code><br /><code>&nbsp; &nbsp;return $this-&gt;hasMany(\App\User\Post::class, 'user_id');</code><br /><code>}</code><br /><br /></li>
<li>Yukarıdaki kod bloğunu a&ccedil;ıklayacak olursak post fonksiyonu Eloquent hasMany ilişkisini d&ouml;nd&uuml;ren bir fonksiyon &ccedil;ağırmakta. hasMany fonksiyonunun ilk arg&uuml;manı hangi modeli kontrol edeceğinizi, ikinci arg&uuml;mansa o model i&ccedil;in veritabanınızda hangi isimle foreign key oluşturduğunuzu g&ouml;steriyor.<br /><br /></li>
<li>Bu işlemleri tamamladıktan sonra &ouml;rnek erişim i&ccedil;in <code>User::where('id', $id)-&gt;first()-&gt;post()-&gt;all()</code> komutunu kullanarak t&uuml;m kullanıcıyla bağdaştırılmış yazıları getirebilirsiniz.<br /><br /></li>
<li>&Ouml;rnek bir yazı oluştururken de <strong>controller</strong>ınızın store fonksiyonu zaten id alıyor, bu id'yi yazılar modelinizde fillable olarak g&ouml;sterirseniz yeni bir yazı oluşturduğunuzda <code>'user_id' =&gt; $id</code> şeklinde g&ouml;nderdiğinizde ilişki kurulmuş olacak.</li>
</ul>
<p>&nbsp;</p>
<p>Umarım anlamanıza yardımcı olmuşumdur, anlamadığınız kısımları yorum b&ouml;l&uuml;m&uuml;nde belirtirseniz yanıtlayacağım. Laravel ile ilgili daha fazla yazı okumak istiyorsanız <a title="Laravel Artisan Komutları" href="/laravel-artisan-komutlari.html" target="_blank" rel="noopener">Laravel Artisan Komutları</a> makaleme de g&ouml;z atmanızı &ouml;neririm.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Linux'ta macOS Tarzı Font Yumuşatma]]></title>
            <link>https://dogukan.dev/linux-ta-macos-tarzi-font-yumusatma</link>
            <guid>https://dogukan.dev/linux-ta-macos-tarzi-font-yumusatma</guid>
            <pubDate>Sun, 20 Oct 2019 13:08:34 GMT</pubDate>
            <description><![CDATA[
Uzun süre macOS kullanıp da başka bir işletim sistemine geçenler varsa yazı tiplerinin daha çirkin durduğunu farkedecektir. Sebebi hem macOS'da kullanılan font yumuşatma ayarları hem de sistem üzerinde kullanılan fontun tür...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5dac216faee2e.png" alt="font smoothing" border="0" /></p>
<p>Uzun s&uuml;re macOS kullanıp da başka bir işletim sistemine ge&ccedil;enler varsa yazı tiplerinin daha &ccedil;irkin durduğunu farkedecektir. Sebebi hem macOS'da kullanılan font yumuşatma ayarları hem de sistem &uuml;zerinde kullanılan fontun t&uuml;r&uuml;.</p>
<p>Şimdi Linux &uuml;zerinde font yumuşatmaları nasıl kurabileceğimize bakalım. &Ouml;ncelikle kendi sistemimden bir fotoğraf atarak g&ouml;stermek istiyorum.</p>
<p><img src="/images/1/5dac2263d2b34.png" alt="font yumuşatma linux" border="0" /></p>
<p>G&ouml;rd&uuml;ğ&uuml;n&uuml;z &uuml;zere sistem yazı tipleri, başlıkların yazı tipleri &ccedil;ok d&uuml;zg&uuml;n ve p&uuml;r&uuml;zs&uuml;z g&ouml;z&uuml;kmekte. Şimdi ayarlarını nasıl yapabileceğinize ge&ccedil;elim.</p>
<ul>
<li>Sistemin genel font ayarlarına giriyoruz.</li>
<li>İnternet &uuml;zerinden San Francisco Display Medium fontunu ttf formatında ediniyoruz. (Link veremiyorum, yasal değil.)</li>
<li>Sistem geneli kurulumunu ger&ccedil;ekleştiriyoruz, ardından se&ccedil;eneklerden SF Pro Display Medium fontunu 10.5pt olarak se&ccedil;iyoruz.</li>
<li>Antialiasing ayarını a&ccedil;ıyoruz.</li>
<li>Sub-pixel rendering ayarını RGB olarak se&ccedil;iyoruz.</li>
<li>Hinting ayarını none olarak se&ccedil;iyoruz.</li>
<li>Font DPI boyutunu 96 olarak ayarlıyoruz.</li>
</ul>
<p>Bu işlemleri KDE ve GNOME masa&uuml;st&uuml; ortamlarında kolaylıkla yapabilirsiniz. Eğer iki ortamdan birini kullanmıyorsanız .Xresources dosyasının nasıl kullanıldığını araştırabilir, kendi sisteminizde de işlemleri o dosya aracılığı ile yapabilirsiniz.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[PHP Instagram API ile Story İşlemleri]]></title>
            <link>https://dogukan.dev/php-instagram-api-ile-story-islemleri</link>
            <guid>https://dogukan.dev/php-instagram-api-ile-story-islemleri</guid>
            <pubDate>Mon, 07 Oct 2019 16:26:23 GMT</pubDate>
            <description><![CDATA[

  Bir arkadaşımın isteği üzerine Instagram API aracılığı ile story yani
  hikayeler üzerinde birkaç işlem yapmam gerekti. Bu sebepten
  ötürü PHP ile Instagram API işlemlerini öğrenmeye karar
  verdim.


  Yapmak istediği ş...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d9b2eadc187b.jpg" alt="Instagram" border="0" /></p>
<p>
  Bir arkadaşımın isteği &uuml;zerine Instagram API aracılığı ile story yani
  hikayeler &uuml;zerinde birka&ccedil; işlem yapmam gerekti. Bu sebepten
  &ouml;t&uuml;r&uuml; PHP ile Instagram API işlemlerini &ouml;ğrenmeye karar
  verdim.
</p>
<p>
  Yapmak istediği şey storylerini g&ouml;ren insanların listesini bir
  veritabanına kaydetmekti. Tam olarak işlemleri bitirmemiş olsam da API'ın
  kullanımını temel olarak anlatıp basit işlemleri nasıl yapacağımızı
  anlatacağım.
</p>
<p>
  &Ouml;ncelikle Instagram API'ı kurmamız gerekmekte. İhtiyacınız olan şey
  sisteminize
  <a title="php" href="https://php.net" target="_blank" rel="noopener"
    >PHP 7.3.9</a
  >
  ve
  <a
    title="composer"
    href="https://getcomposer.org"
    target="_blank"
    rel="noopener"
    >Composer</a
  >
  kurulumlarını ger&ccedil;ekleştirmek. Bu işlemleri yaptıktan sonra yeni
  projemizi oluşturmaya başlayabiliriz.
</p>
<p>
  Projemizin i&ccedil;inde bulunacağı klas&ouml;r&uuml; oluşturun (Linux
  i&ccedil;in <code>mkdir &lt;projeAdı&gt;</code> komutunu kullanabiliriz).
  Ardından terminal, konsol veyahut ne kullanıyorsanız o uygulamayı a&ccedil;ın
  ve proje dizinine girdikten sonra (<code>cd </code>),
  <code>composer require mgp25/instagram-php</code> komutu ile Instagram API
  dosyalarını projemize dahil edelim.
</p>
<p>
  Bu işlemleri tamamladıktan sonra artık kodlarımızı yazacağımız dosyamızı
  oluşturabiliriz. Kendinize bir php dosyası a&ccedil;ın (&ouml;r:
  <code>instagram-islemleri.php</code>). Dosyayı oluşturduktan sonra php
  taglarını a&ccedil;ın ve yapmamız gereken bir ka&ccedil; rutin işlem mevcut.
</p>
<p>
  &Ouml;ncelikle hata kodlarını g&ouml;rmemiz gerekecek. Lokal sunucunun 500
  hatası vermeden hataları g&ouml;stermesi i&ccedil;in:
</p>
<p>
  <code>error_reporting(E_ALL);</code><br /><code
    >ini_set('display_errors', 'On');</code
  >
</p>
<p>
  komutlarını php taglarımız arasına ekliyoruz. Bu b&uuml;t&uuml;n hataları
  g&ouml;rmemizi ve d&uuml;zenlememizi sağlayacak.
</p>
<p>
  Composer aracılığı ile eklediğimiz kodların &ccedil;alışması i&ccedil;in ise
  <code>require 'vendor/autoload.php';</code> komutunu girmemiz yeterli, bu
  satırı ekledikten sonra composer ile eklediğiniz t&uuml;m paketler sorunsuzca
  projenizde &ccedil;alışmaya başlayacaktır.
</p>
<p>
  Başlamak i&ccedil;in gereken t&uuml;m işlemleri hallettiğimize g&ouml;re işe
  koyulabiliriz. &Ouml;ncelikle API unofficial yani gayriresmi olduğundan yazarı
  bizim riskleri kabul etmemizi istemiş. API classındaki kodların
  &ccedil;alışabilmesi i&ccedil;in
  <code
    ><span class="pl-c1">\InstagramAPI\</span
    ><span class="pl-c1">Instagram</span><span class="pl-k">::</span
    ><span class="pl-smi">$allowDangerousWebUsageAtMyOwnRisk</span>
    <span class="pl-k">=</span> <span class="pl-c1">true</span>;</code
  >
  satırını kodumuza ekliyoruz. Artık API işlemlerini sorunsuzca
  ger&ccedil;ekleştirmeye başlayabiliriz.
</p>
<p>
  Classımızdan yeni bir obje &uuml;retmek i&ccedil;in
  <code>$ig = new \InstagramAPI\Instagram(true, true);</code> kodunu
  kullanıyoruz. Arg&uuml;manların anlamlarını a&ccedil;ıklamak gerekirse birinci
  arg&uuml;man debug aktif ediyor ikinci arg&uuml;man ise debugdaki gereksiz
  t&uuml;m kod par&ccedil;acıklarını kısaltıyor (truncate).&nbsp;
</p>
<p>
  Bu işlemi de tamamladıktan sonra artık giriş yapmaya ge&ccedil;ebiliriz ama
  yapmamız gereken bir şey var, development işlemlerini hangi hesabınız ile
  ger&ccedil;ekleştirecekseniz
  <a
    title="Instagram User ID"
    href="https://codeofaninja.com/tools/find-instagram-user-id"
    target="_blank"
    rel="noopener"
    >bu adresten</a
  >
  Instagram Developer User ID'nizi &ouml;ğrenmeniz gerekmektedir.
</p>
<p>
  User ID'yi edindiğimize g&ouml;re &uuml;&ccedil; değişken oluşturalım bunlar
  <code>$username</code>, <code>$password</code>,
  <code>$user_id</code> değişkenleri olabilir. İsimleri T&uuml;rk&ccedil;e veya
  İngilizce olarak siz se&ccedil;ebilirsiniz (<code>$kullanici_adi</code>,
  <code>$sifre</code>, <code>$kullanici_id</code>). Bu değişkenlerin
  i&ccedil;eriğini stringler ile tanımlayarak yazmanız gerekmekte. (&ouml;r:
  <code>$username = 'dogukan.jpg'</code>)
</p>
<p>
  Giriş yapmak i&ccedil;in bir try, catch bloğu tanımlayın. Catch bloğunun
  arg&uuml;manı <code>\Exception $e</code> olursa kolayca hata yakalama
  işlemlerini ger&ccedil;ekleştirebiliriz. try bloğunun i&ccedil;erisine
  <code>$ig-&gt;login($username, $password);</code> komutunu yazdığınızda API'a
  login olacaksınız.&nbsp;
</p>
<p>
  T&uuml;m işlemleri yaptığımıza g&ouml;re sayfayı yenilediğinizde hata
  almıyorsanız story yani hikayeler ile oynamaya başlayabiliriz. Benim yapmak
  istediğim giriş yaptığım kullanıcının hikayelerini alıp o hikayeleri izleyen
  kişileri veritabanına kaydetmek olduğundan bunun i&ccedil;in gereken temel
  komutlara bir g&ouml;z atalım.
</p>
<h3>Instagram API Hikaye (Story) İşlemleri</h3>
<ul>
  <li>
    Takip ettiğiniz kişilerin hikayelerini g&ouml;rmek i&ccedil;in:<br /><code
      >$ig-&gt;story-&gt;getReelsTrayFeed();</code
    ><br /><br />
  </li>
  <li>
    Kullanıcının hikayelerini g&ouml;rmek i&ccedil;in:<br /><code
      >$ig-&gt;story-&gt;getUserReelMediaFeed('user_id');</code
    ><br /><sub
      >* Yukarıda belirttiğim şekilde aldığımız user_id'den bahsediliyor.<br /><br
    /></sub>
  </li>
  <li>
    Hikayenize bakan kişileri g&ouml;rmek i&ccedil;in:<br /><code
      >$ig-&gt;story-&gt;getStoryItemViewers('story_id');</code
    ><br /><sub
      >* story_id parametresini getUserReelMediaFeed fonksiyonu ile alacağız.
      Makalenin devamında bahsedeceğim.</sub
    ><sub><br /></sub>
  </li>
</ul>
<p>
  &Ouml;rnek olarak giriş yapıp storyleri aldığım ve onlardan gelen
  kullanıcıları bastırdığım a&ccedil;ıklamalı kodu inceleyebilirsiniz.
</p>
<p><gist id="bacf92b85449ef8e6f885e0d77dc77c3"></gist></p>
<p>
  Sorularınız i&ccedil;in yorum b&ouml;l&uuml;m&uuml;ne başvurabilirsiniz.
  Instagram API hakkında yeni makaleler ekleyeceğim. Sıradaki planım mesaj
  işlemlerini detaylı olarak &ouml;rnekli anlatmak. Okuduğunuz i&ccedil;in
  teşekk&uuml;r ederim.
</p>
]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[VoodooHDA ile Mojave Üzerinde Ses Tanıtmak]]></title>
            <link>https://dogukan.dev/voodoohda-ile-mojave-uzerinde-ses-tanitmak</link>
            <guid>https://dogukan.dev/voodoohda-ile-mojave-uzerinde-ses-tanitmak</guid>
            <pubDate>Tue, 10 Sep 2019 12:42:13 GMT</pubDate>
            <description><![CDATA[
Sistemim üzerinde macOS kullanmak istediğimden ancak bir Macbook da satın almak istemediğimden Hackintosh konusu ilgimi çekti. Ben de araştırmalar yapmaya başladım. Bu rehberi yazarken öncelikle osxinfo.net forumundaki herk...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d775ebc2e3e1.png" alt="VoodooHDA" border="0" /></p>
<p>Sistemim &uuml;zerinde macOS kullanmak istediğimden ancak bir Macbook da satın almak istemediğimden Hackintosh konusu ilgimi &ccedil;ekti. Ben de araştırmalar yapmaya başladım. Bu rehberi yazarken &ouml;ncelikle osxinfo.net forumundaki herkese &ccedil;ok teşekk&uuml;r ediyorum, başarılı bir kurulum ger&ccedil;ekleştirmemi sağladılar. İsterseniz kurulum aşamasına ge&ccedil;elim.</p>
<p>İlk olarak <a href="https://sourceforge.net/projects/voodoohda/">https://sourceforge.net/projects/voodoohda/</a> adresinden VoodooHDA'nın en g&uuml;ncel s&uuml;r&uuml;m kextini indiriyoruz.</p>
<p>&nbsp;</p>
<p>Ardından sistemimizden AppleHDA.kext dosyasını kaldırmamız gerekmekte, aksi halde VoodooHDA aktif olmayacaktır. Bu işlemi ger&ccedil;ekleştirmek i&ccedil;in terminali a&ccedil;ıyoruz ve aşağıdaki komutu giriyoruz.</p>
<p><img src="/images/1/5d776001e21fb.png" alt="zsh" border="0" /></p>
<p><code>sudo rm -r /System/Library/Extensions/AppleHDA.kext/</code></p>
<p>&nbsp;</p>
<p>Bu işlemi tamamladıktan sonra herhangi bir kext y&uuml;kleme programı ile VoodooHDA.kext dosyasını sisteminize kurmanız gerekiyor. Ben kext y&uuml;kleme programı i&ccedil;in KCPM Utility Proyu tercih ediyorum. <a href="https://www.firewolf.science/2016/09/kcpm-utility-pro-v6-brand-new-kexts-ezinstaller-macos-sierra-supported-repairing-permissions-configuring-rootless-and-more/" target="_blank" rel="noopener">Bu link</a>ten programı edinebilirsiniz.</p>
<p><img src="/images/1/5d7760f594bc2.png" alt="KCPM Utility Pro" border="0" /></p>
<p>Programın ayarlarını fotoğraftaki gibi yapıyoruz. /Library/Extensions se&ccedil;eneğinin se&ccedil;ilmiş olması &ouml;nemlidir. Ardından g&ouml;rd&uuml;ğ&uuml;m&uuml;z EZ Mode Enabled yazısının oraya VoodooHDA.kext dosyasını s&uuml;r&uuml;kl&uuml;yoruz. İşlemler otomatik olarak yapılıyor, kextcache yeniden build ediliyor. AppleHDA'nın silme işlemi de bu şekilde tamamlanmış oluyor.</p>
<p>&nbsp;</p>
<p>Sonu&ccedil; olarak sistemi yeniden başlattığımızda problemsiz olarak sesiniz aktifleşmiş olacaktır. Hackintoshunuzda m&uuml;zik dinlemenin keyfini &ccedil;ıkartın :)</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Laravel zvalIsRef Hatası Çözümü]]></title>
            <link>https://dogukan.dev/laravel-zvalisref-hatasi-cozumu</link>
            <guid>https://dogukan.dev/laravel-zvalisref-hatasi-cozumu</guid>
            <pubDate>Tue, 03 Sep 2019 01:09:12 GMT</pubDate>
            <description><![CDATA[
Bugün Laravel'de karşılaştığım zvalIsRef hatasının çözümünü anlatacağım. Hata ile karşılaşırsanız yazdığınız kod eğer hatalıysa bu hatayı alacaksınız ve nerede yanlış yaptığınızı farkedemeyeceksiniz.
Projemde bunu düzeltme...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d43eefec9226.jpg" alt="Laravel" border="0" /></p>
<p>Bug&uuml;n Laravel'de karşılaştığım zvalIsRef hatasının &ccedil;&ouml;z&uuml;m&uuml;n&uuml; anlatacağım. Hata ile karşılaşırsanız yazdığınız kod eğer hatalıysa bu hatayı alacaksınız ve nerede yanlış yaptığınızı farkedemeyeceksiniz.</p>
<p>Projemde bunu d&uuml;zeltmek i&ccedil;in dosyaları tek tek elle girdim, kodları sıfırdan yazmaya &ccedil;alıştım gibi gibi ancak sorun &ccedil;&ouml;z&uuml;lmedi.</p>
<p>Problemin &ccedil;&ouml;z&uuml;m&uuml; aslında &ccedil;ok basit. Laravel 5.8 s&uuml;r&uuml;m&uuml; PHP 8.0 ve 7.4 ile &ccedil;alışmıyor. PHP s&uuml;r&uuml;m&uuml;n&uuml;z&uuml; <strong>7.3.9</strong>'a d&uuml;ş&uuml;r&uuml;rseniz projeniz problemsiz şekilde &ccedil;alışacak, hataları g&ouml;sterecektir.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Arch Linux Elantech Touchpad is Not Working]]></title>
            <link>https://dogukan.dev/arch-linux-elantech-touchpad-not-working</link>
            <guid>https://dogukan.dev/arch-linux-elantech-touchpad-not-working</guid>
            <pubDate>Sat, 31 Aug 2019 02:45:43 GMT</pubDate>
            <description><![CDATA[
Arch Linux üzerine gelen yeni güncelleme ile xf86-input-synaptics paketi kaldırıldığından Elantech marka touchpad kullanan bilgisayarlarda touchpad çalışmamaya başladı.
Bilgisayarım MSI GS73VR bu hatadan nemalanınca proble...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d69a5bea9b1e.jpg" alt="Elantech Touchpad Arch Linux" border="0" /></p>
<p>Arch Linux &uuml;zerine gelen yeni g&uuml;ncelleme ile xf86-input-synaptics paketi kaldırıldığından Elantech marka touchpad kullanan bilgisayarlarda touchpad &ccedil;alışmamaya başladı.</p>
<p>Bilgisayarım MSI GS73VR bu hatadan nemalanınca problemi araştırmaya başladım. İlk &ouml;nce eski paketi kaldırarak başladım, pacman &uuml;zerinden logları inceleyerek neler update edilmiş onları inceledim. Libinput paketini ve &ccedil;eşitli paketleri downgrade ettim ancak problem bir t&uuml;rl&uuml; &ccedil;&ouml;z&uuml;lmedi.</p>
<p>rmmod psmouse ve modprobe psmouse proto=imps yazmak touchpadi &ccedil;alıştırsa da kullanılmaz bir haldeydi. &Ccedil;oklu dokunuş desteğini kazandırmam gerekiyordu.</p>
<p>bugs.archlinux.org &uuml;zerinde y&uuml;r&uuml;tt&uuml;ğ&uuml;m araştırmalarda benim gibi bir &ccedil;ok kullanıcının problemi yaşadığını g&ouml;rd&uuml;m. &Ccedil;ok basit bir &ccedil;&ouml;z&uuml;mle sorunu halledebilirsiniz. <strong>(ps. Commands that written below fixes the touchpad issue)</strong></p>
<p><code>yay -R xf86-input-synaptics</code></p>
<p><code>options psmouse elantech_smbus=0 synaptics_intertouch=0 &gt;&gt; /etc/modprobe.d/elan.conf</code>&nbsp; <strong>(root olarak &ccedil;alıştırın)</strong></p>
<p>Sisteminizi yeniden başlattığınızda problem &ccedil;&ouml;z&uuml;lecektir.&nbsp;</p>
<p>Kaynak: <a href="https://bugs.archlinux.org/task/63525">https://bugs.archlinux.org/task/63525</a></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[MySQL "Specified key was too long" Hatası]]></title>
            <link>https://dogukan.dev/mysql-specified-key-was-too-long-hatasi</link>
            <guid>https://dogukan.dev/mysql-specified-key-was-too-long-hatasi</guid>
            <pubDate>Sun, 25 Aug 2019 22:25:22 GMT</pubDate>
            <description><![CDATA[
Sitemin veritabanının yedeğini almıştım ve başka bir ortama taşımam gerekiyordu. Dosyaları taşıdım, veritabanını oluşturdum, SQL dump aldıktan sonra yeni sunucuya yükledim ki bu hata oluştu. (#1071 - Specified key was too l...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d62ccb3b8c58.jpg" alt="MariaDB" border="0" /></p>
<p>Sitemin veritabanının yedeğini almıştım ve başka bir ortama taşımam gerekiyordu. Dosyaları taşıdım, veritabanını oluşturdum, SQL dump aldıktan sonra yeni sunucuya y&uuml;kledim ki bu hata oluştu. (#1071 - Specified key was too long; max key length is 767 bytes)</p>
<p><img src="/images/1/5d62cc717c226.jpg" alt="Specified key was too long, max key length is 767 bytes" border="0" data-featherlight="/images/1/5d62cc717c226.jpg" /></p>
<p>&nbsp;</p>
<p>Bu hata b&uuml;y&uuml;k ihtimal ile Laravel uygulamalarınızda karşınıza &ccedil;ıkacak. Uzun uğraşlar sonucu sorunu &ccedil;&ouml;zd&uuml;m. İlk olarak halihazırdaki veritabanlarınız i&ccedil;in &ccedil;&ouml;z&uuml;m&uuml; sunacağım, ikinci yol olaraksa veritabanı oluşturulmamış Laravel uygulamalarınızda bu hata ile asla karşılaşmamanızı sağlayacağım. Test etmediğim ancak işe yaradığından bahsedilen &uuml;&ccedil;&uuml;nc&uuml; bir yol daha g&ouml;stereceğim.</p>
<p>&nbsp;</p>
<h2>MySQL veya MariaDB s&uuml;r&uuml;m&uuml;n&uuml; y&uuml;kseltmek</h2>
<p>Evet hatanın ilk &ccedil;&ouml;z&uuml;m&uuml; bu. Sunucunuzdaki MySQL s&uuml;r&uuml;m&uuml; 5.7.7 ve altındaysa, MariaDB de 10.2.2 ve altındaysa bu hatayla karşılaşacaksınız.&nbsp;</p>
<p>G&uuml;ncelleme yapmak i&ccedil;in &ouml;rnek olarak CentOS 7.5 MariaDB 10.1'den 10.4'e nasıl g&uuml;ncelleyeceğinizi g&ouml;stereceğim.</p>
<p><code>systemctl stop mysql</code></p>
<p><code>yum -y remove mariadb-server</code></p>
<p><code>sed -i 's|10.1|10.4|g' /etc/yum.repos.d/MariaDB.repo</code></p>
<p><code>yum -y install mariadb-server</code></p>
<p><code>systemctl start mysql</code></p>
<p><code>mysql_upgrade -u root -p&nbsp;</code></p>
<p>komutlarını sırasıyla &ccedil;alıştırdığımızda MariaDB g&uuml;ncellenecektir.</p>
<p>&nbsp;</p>
<h2>Laravel Framework defaultStringLength D&uuml;zenleme</h2>
<p>Laravel'de Schema classına tanımlanmış varsayılan string uzunluğunu değiştirerek bu problemi &ccedil;&ouml;zebiliriz.</p>
<p><code>app/Providers/AppServiceProvider.php</code> dosyasını d&uuml;zenliyoruz.</p>
<p><code>use Illuminate\Support\Facades\Schema;</code></p>
<p><code>public function boot() {</code><br /><code>&nbsp; &nbsp; Schema::defaultStringLength(191);</code><br /><code>}</code></p>
<p>eklemesini yaptığımızda bundan sonra projelerinizde problem &ccedil;ıkmayacaktır.</p>
<p>Hatanın oluşma sebebi Laravel varsayılan olarak emojileri de veritabanında saklayan utf8mb4 karakter setini kullandığı i&ccedil;in bu karakter setinin &uuml;rettiği string uzunluklarını eski s&uuml;r&uuml;mler destekleyemeyebiliyor. Hatanın d&uuml;zenlenmesi i&ccedil;in AppServiceProviderınızda bu d&uuml;zenlemeyi yaptıktan sonra tekrar migrate etmeniz gerekiyor.</p>
<p>&nbsp;</p>
<h2>innodb_large_prefix &ouml;zelliğini etkinleştirmek</h2>
<p>my.ini dosyanızı a&ccedil;ıp aşağıdaki satırları i&ccedil;erisine ekleyin. Dosyanın konumu sisteminize g&ouml;re değişiklik g&ouml;sterebilir.</p>
<p><code>[mysqld]</code><br /><code>innodb_file_format = Barracuda</code><br /><code>innodb_large_prefix = 1</code><br /><code>innodb_file_per_table = ON</code></p>
<p>&nbsp;</p>
<p>Probleminiz &ccedil;&ouml;z&uuml;lmediyse yorumlar b&ouml;l&uuml;m&uuml;nde belirtebilirsiniz, yardımcı olmaya &ccedil;alışacağım.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[CentOS 7 CyberPanel Kurulumu]]></title>
            <link>https://dogukan.dev/centos-7-cyberpanel-kurulumu</link>
            <guid>https://dogukan.dev/centos-7-cyberpanel-kurulumu</guid>
            <pubDate>Wed, 07 Aug 2019 14:17:19 GMT</pubDate>
            <description><![CDATA[
CyberPanel kurulumu oldukça basittir. Kurulumu "root" hesap olarak çalıştırmalısınız.
Sistem gereksinimleri

CentOS 7.x (minimal versiyon tavsiye edilir)
Python 2.7
1024MB minimum RAM
10GB minimum disk

 
Cyberpane...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d4aa4af8e693.png" border="0" alt="Cyberpanel" itemprop="image" data-featherlight="/images/1/5d4aa4af8e693.png" /></p>
<p>CyberPanel kurulumu olduk&ccedil;a basittir. Kurulumu <strong>"root"</strong> hesap olarak &ccedil;alıştırmalısınız.</p>
<h2>Sistem gereksinimleri</h2>
<ul>
<li>CentOS 7.x (minimal versiyon tavsiye edilir)</li>
<li>Python 2.7</li>
<li>1024MB minimum RAM</li>
<li>10GB minimum disk</li>
</ul>
<h2>&nbsp;</h2>
<h2>Cyberpanel kurulumu</h2>
<p>Cyberpanel'i tek komut ile kurabilirsiniz. Kurmak i&ccedil;in root olarak login olduğunuz sisteminizde bu komutu &ccedil;alıştırın:</p>
<p><code>sh &lt;(curl https://cyberpanel.net/install.sh || wget -O - https://cyberpanel.net/install.sh)</code></p>
<p>Gerekli adımları izledikten sonra paneliniz kullanıma hazır!</p>
<p>&nbsp;</p>
<h2>Nasıl kullanılır?</h2>
<p>https://:8090<br /><strong>Kullanıcı adı:</strong> admin<br /><strong>Şifre:</strong> 1234567</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Arch Linux KDE Plasma Kurulumu]]></title>
            <link>https://dogukan.dev/arch-linux-kde-plasma-kurulumu</link>
            <guid>https://dogukan.dev/arch-linux-kde-plasma-kurulumu</guid>
            <pubDate>Wed, 07 Aug 2019 12:16:27 GMT</pubDate>
            <description><![CDATA[Daha önce bahsettiğimiz Arch Linux Kurulumu makalemizde Desktop Environment yani masaüstü ortamı kurulumu yapmadan anlatımı bitirmiştik. Bugün ise Arch Linux'a KDE Plasma kurulumu yapacağız. Adımları sıfır kurulum yaptığınızı...]]></description>
            <content:encoded><![CDATA[<p>Daha &ouml;nce bahsettiğimiz <a title="Arch Linux Kurulumu" href="/arch-linux-kurulumu-resimli-anlatim.html" target="_blank" rel="noopener">Arch Linux Kurulumu</a> makalemizde <strong>Desktop Environment</strong> yani masa&uuml;st&uuml; ortamı kurulumu yapmadan anlatımı bitirmiştik. Bug&uuml;n ise <strong>Arch Linux</strong>'a <strong>KDE Plasma</strong> kurulumu yapacağız. Adımları sıfır kurulum yaptığınızı varsayarak anlatacağım.</p>
<p><img src="/images/1/5d4a888f87143.jpg" alt="KDE Plasma " border="0" data-featherlight="/images/1/5d4a888f87143.jpg" /></p>
<h2>Kuruluma başlangı&ccedil;</h2>
<h3>Xorg kurulumu</h3>
<p><strong>Xorg video server</strong> g&ouml;revini yapan, CLI yerine GUI işlemlerini ger&ccedil;ekleştiren altyapımız.</p>
<p><code>yay -S xorg-server</code> komutu ile kurulumu ger&ccedil;ekleştirilebilir.</p>
<p>&nbsp;</p>
<h3>Video driver kurulumu</h3>
<p>Intel GPU'lar i&ccedil;in &ouml;rnek olarak alttaki kurulumu izleyebiliriniz. Laptopunuza kuruyorsanız zaten tek se&ccedil;eneğiniz bu.</p>
<p><code>yay -S xf86-video-intel mesa</code> komutu ile driver kurulumunu yapıyoruz.</p>
<p>&nbsp;</p>
<h3>SDDM kurulumu (greeter, login ekranı)</h3>
<p>SDDM her sistem başlangıcında size login ekranı sunan servis yazılımıdır.</p>
<ul>
<li><code>yay -S sddm</code></li>
<li><code>systemctl enable sddm</code></li>
<li><code>systemctl start sddm</code></li>
</ul>
<p>&nbsp;</p>
<h3>KDE Plasma kurulumu</h3>
<p>Plasma kurulumunu ger&ccedil;ekleştirelim. Evet siz de biliyorsunuz en iyisi KDE :)</p>
<p><code>pacman -S plasma</code> komutu ile kurulumu yapalım. Default se&ccedil;enekler ile ilerleyin.</p>
<p>&nbsp;</p>
<h3>NetworkManager ile ağ y&ouml;netimi</h3>
<p>NetworkManager ve plasma-nm kurulumunu yapalım.</p>
<p><code>pacman -S networkmanager</code> ve <code>pacman -S plasma-nm</code> ile kurulumları yapalım.</p>
<p>&nbsp;</p>
<h3>T&uuml;m KDE uygulamalarının kurulumu</h3>
<p>T&uuml;m KDE uygulamalarını kurmak i&ccedil;in <code>pacman -S kde-applications-meta</code> komutunu kullanalım.İstediğiniz uygulamaları se&ccedil;ebilirsiniz.</p>
<p>&nbsp;</p>
<h3>Chrome kurulumu</h3>
<p><code>pacman -S chromium</code> ile Chrome kurulumu yapabilirsiniz.</p>
<p>&nbsp;</p>
<p><strong>KDE Plasma</strong> kurulumu tamamlanmıştır. Sistemde &ccedil;ıkan problemler hakkında yorum atarsanız yardımcı olacağım ayrıca eksik g&ouml;rd&uuml;ğ&uuml;n&uuml;z kısımları da belirtirseniz makaleyi g&uuml;ncellemekten mutluluk duyarım.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[PHP ile TCMB Döviz Kuru Çevirici]]></title>
            <link>https://dogukan.dev/php-ile-tcmb-doviz-kuru-cevirici</link>
            <guid>https://dogukan.dev/php-ile-tcmb-doviz-kuru-cevirici</guid>
            <pubDate>Tue, 06 Aug 2019 11:57:26 GMT</pubDate>
            <description><![CDATA[
Daha öncesinde yazdığım TCMB Döviz Kuru çevirici PHP fonksiyonunu sizlerle paylaşmak istiyorum. Kolay anlaşılabilmesi için bloklara böldüm ve her blok için açıklama satırları yazdım.



Fonksiyonu kullanabilmek için:TCM...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d492a7a3f800.jpg" border="0" alt="TCMB D&ouml;viz Kuru &Ccedil;evirici" itemprop="image" /></p>
<p>Daha &ouml;ncesinde yazdığım <strong>TCMB D&ouml;viz Kuru &ccedil;evirici</strong> PHP fonksiyonunu sizlerle paylaşmak istiyorum. Kolay anlaşılabilmesi i&ccedil;in bloklara b&ouml;ld&uuml;m ve her blok i&ccedil;in a&ccedil;ıklama satırları yazdım.</p>
<p>
<script src="https://gist.github.com/dogukanoksuz/37b42daec5c64aaceb65cd29c9d2fb26.js"></script>
</p>
<p>Fonksiyonu kullanabilmek i&ccedil;in:<br /><code>TCMB_Converter('from', 'to', value);</code></p>
<p>&Ouml;rnek kullanım:<br /><code>TCMB_Converter('TRY', 'USD', 100);</code></p>
<p>&nbsp;</p>
<p>İyi kullanımlar.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[SSH Olmadan Artisan Kullanımı]]></title>
            <link>https://dogukan.dev/ssh-olmadan-artisan-kullanimi</link>
            <guid>https://dogukan.dev/ssh-olmadan-artisan-kullanimi</guid>
            <pubDate>Tue, 06 Aug 2019 11:03:57 GMT</pubDate>
            <description><![CDATA[
Laravel ile bir uygulama yazdınız ancak paylaşımlı bir hostingde mi kullanacaksınız? Büyük ihtimal ile Artisan komutlarını çalıştırmadan uygulamanızı düzgün şekilde kullanamayacaksınız. Artisan komutlarını çağırmak için yap...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d491f5deaba3.jpeg" alt="Laravel 5.8" border="0" /></p>
<p>Laravel ile bir uygulama yazdınız ancak paylaşımlı bir hostingde mi kullanacaksınız? B&uuml;y&uuml;k ihtimal ile Artisan komutlarını &ccedil;alıştırmadan uygulamanızı d&uuml;zg&uuml;n şekilde kullanamayacaksınız. Artisan komutlarını &ccedil;ağırmak i&ccedil;in yapmanız gereken işlemler aslında &ccedil;ok basit.</p>
<p><code>Route::get('/admin/artisan-route-cache', function() {<br />&nbsp; &nbsp; Artisan::call('route:cache');<br />});</code></p>
<p>&Ouml;rnek bir rota i&ccedil;erisinde <code>Artisan::call('');</code> şeklinde kullanımını g&ouml;rd&uuml;k. Bu şekilde kolayca Artisan komutlarını &ccedil;alıştırabilirsiniz.&nbsp;</p>
<p><strong>&Ouml;NEMLİ:</strong> Bunları admin panelinizde d&uuml;ğmeler olarak ekleyecekseniz bir middleware i&ccedil;erisinde kontrol ettirmenizde fayda var. &Ouml;nemli komutları a&ccedil;ık halde bırakırsanız tehlikeli durumlar yaratabilir.</p>
<p><strong>Middleware ile nasıl kontrol ederim?</strong></p>
<p><code>Route::group(['middleware' =&gt; ['auth', 'CheckIfAdmin']], function() {<br />&nbsp; &nbsp; Route::get('/admin/artisan-route-cache', function() {<br />&nbsp; &nbsp; &nbsp; &nbsp; Artisan::call('route:cache');<br />&nbsp; &nbsp; });<br />});</code></p>
<p>Yaptığımız şey benim &ouml;zel yazdığım CheckIfAdmin middlewareından ve auth middlewareından ge&ccedil;irmek oldu. B&ouml;ylece dışarıdan kullanıcılar bu linki bilse bile erişim sağlayamayacak.</p>
<p>İyi kodlamalar!</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Laravel Artisan Komutları]]></title>
            <link>https://dogukan.dev/laravel-artisan-komutlari</link>
            <guid>https://dogukan.dev/laravel-artisan-komutlari</guid>
            <pubDate>Fri, 02 Aug 2019 12:06:48 GMT</pubDate>
            <description><![CDATA[Laravel Artisan nedir?
Artisan Laravel paketine dahil olarak gelen komut satırı aracıdır(CLI). Laravel projelerinizi oluştururken fazlasıyla kullanacağınız, işleri kolaylaştıran bir araçtır.

 
Laravel Artisan nasıl kulla...]]></description>
            <content:encoded><![CDATA[<h2>Laravel Artisan nedir?</h2>
<p>Artisan Laravel paketine dahil olarak gelen komut satırı aracıdır(CLI). Laravel projelerinizi oluştururken fazlasıyla kullanacağınız, işleri kolaylaştıran bir ara&ccedil;tır.</p>
<p><img src="/images/1/5d43eefec9226.jpg" alt="Laravel framework" border="0" /></p>
<h2>&nbsp;</h2>
<h2>Laravel Artisan nasıl kullanılır?</h2>
<p><code>php artisan [se&ccedil;enek] [arg&uuml;manlar]</code> şeklinde &ccedil;alıştırılmaktadır. &Ccedil;alışması i&ccedil;in proje dizininde olmanız gerekmektedir ve sisteminizde PHP kurulu olmalıdır.</p>
<p>&nbsp;</p>
<h2>Artisan komutları</h2>
<ul>
<li><code>php artisan list</code><br />Artisan &uuml;zerinde kullanabileceğiniz t&uuml;m komutları listeler.<br /><br /></li>
<li><code>php artisan --version</code><br />Laravel uygulamanızın s&uuml;r&uuml;m&uuml;n&uuml; g&ouml;sterir.<br /><br /></li>
<li><code>php artisan down</code><br />Uygulamanızı bakım moduna alır.<br /><br /></li>
<li><code>php artisan env</code><br />Bulunduğunuz "environment"i g&ouml;sterir.<br /><br /></li>
<li><code>php artisan migrate</code><br />Veritabanı eşitleme işlemlerini yapar.<br /><br /></li>
<li><code>php artisan migrate:rollback</code><br />Veritabanında yaptığınız değişiklikleri adım adım geri alır.<br /><br /></li>
<li><code>php artisan migrate:refresh</code><br />B&uuml;t&uuml;n yaptığınız değişiklikleri geri alır ve veritabanını yeniden y&uuml;kler. (Verileriniz silinir)</li>
<li><code>php artisan migrate:refresh --seed</code><br />Refresh işlemini aynen ger&ccedil;ekleştirir ve database seederlarını da &ccedil;alıştırır.<br /><br /></li>
<li><code>php artisan serve</code><br />Uygulamanızı başlatır.<br /><br /></li>
<li><code>php artisan up</code><br />Uygulamanızı bakım modundan &ccedil;ıkartır.<br /><br /></li>
<li><code>php artisan cache:clear</code><br />Uygulama &ouml;nbelleğini temizler.<br /><br /></li>
<li><code>php artisan cache:table</code><br />Uygulamanızda veritabanı i&ccedil;in &ouml;nbellekleme oluşturur.<br /><br /></li>
<li><code>php artisan config:cache</code><br />Konfig&uuml;rasyon dosyanızı hızlı y&uuml;kleme i&ccedil;in &ouml;nbellekler.<br /><br /></li>
<li><code>php artisan config:clear</code><br />Konfig&uuml;rasyon &ouml;nbelleğini temizler.<br /><br /></li>
<li><code>php artisan make:auth</code><br />Laravel'in i&ccedil;erisinde gelen hazır kullanıcı sistemini uygulamanıza ekler.<br /><br /></li>
<li><code>php artisan make:controller &lt;Dizin/ControllerAdı&gt;</code><br />Sizin i&ccedil;in istediğiniz isimde hazır bir controller oluşturur.<br /><br /></li>
<li><code>php artisan make:controller &lt;Dizin/ControllerAdı&gt; --resource</code><br />Hazır controller kalıbı ile controller oluşturmanızı sağlar.<br /><br /></li>
<li><code>php artisan make:migration &lt;MigrationAdı&gt;</code><br />Veritabanı i&ccedil;in migration dosyası oluşturur.<br /><br /></li>
<li><code>php artisan make:model &lt;ModelAdı&gt;</code><br />Yeni bir Eloquent model nesnesi oluşturur.<br /><br /></li>
<li><code>php artisan route:list</code><br />Eklediğiniz ve vendor uygulamaların eklediği rotaları listeler.<br /><br /></li>
<li><code>php artisan route:clear</code><br />Rota &ouml;nbelleğini temizler.<br /><br /></li>
<li><code>php artisan route:cache</code><br />Rotaları hızlı y&uuml;kleme i&ccedil;in &ouml;nbellekler.<br /><br /></li>
<li><code>php artisan view:clear</code><br />Derlenmiş &ouml;ny&uuml;z dosyalarını temizler. Bazen değişiklikler sayfada g&ouml;z&uuml;kmeyince gereklidir.</li>
</ul>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Arch Linux Kurulumu Resimli Anlatım]]></title>
            <link>https://dogukan.dev/arch-linux-kurulumu-resimli-anlatim</link>
            <guid>https://dogukan.dev/arch-linux-kurulumu-resimli-anlatim</guid>
            <pubDate>Thu, 01 Aug 2019 13:00:00 GMT</pubDate>
            <description><![CDATA[*Güncelleme 26.03.2020: Arch Linux Kurulumu Videolu Anlatım olarak siteme ekledim. Linke tıklayarak ziyaret edebilirsiniz!*
Öncelikle Arch ISO indirmemiz gerekiyor.
https://www.archlinux.org/download/ adresinden Direct down...]]></description>
            <content:encoded><![CDATA[<p><strong>*G&uuml;ncelleme 26.03.2020: <a href="/arch-linux-kurulumu-videolu-anlatim.html" target="_blank" rel="noopener">Arch Linux Kurulumu Videolu Anlatım</a> olarak siteme ekledim. Linke tıklayarak ziyaret edebilirsiniz!*</strong></p>
<p>&Ouml;ncelikle Arch ISO indirmemiz gerekiyor.</p>
<p><a href="https://www.archlinux.org/download/">https://www.archlinux.org/download/</a> adresinden Direct download kısmından kendinize uyan isoyu indirip Rufus (<a href="https://rufus.ie/">https://rufus.ie/</a>), BalenaEtcher gibi yazılımlar ile bootable flash oluşturabilirsiniz. GPT modunu se&ccedil;meniz gerekmektedir. İmajı oluşturduktan sonra sisteminizi flashtan boot etmeniz gerek, ancak Secure Boot se&ccedil;eneğini devre dışı bırakmalısınız.&nbsp;</p>
<p>Boot ettikten sonra g&ouml;receğimiz manzara:</p>
<p><img src="/images/1/5d429f3697844.png" alt="Arch Linux Boot Screen" border="0" data-featherlight="/images/1/5d429f3697844.png" /></p>
<p>&nbsp;</p>
<p>İlk se&ccedil;eneği se&ccedil;erek siyah korkun&ccedil;(!) kurulum ekranımızı başlatıyoruz.</p>
<p><img src="/images/1/5d429f8d917e0.png" alt="Arch Linux" border="0" data-featherlight="/images/1/5d429f8d917e0.png" /></p>
<p>Google'u pingleyerek internetimiz var mı diye kontrol ediyoruz, kurulum boyunca bir ethernet bağlantısına ihtiyacınız var. Ping &ccedil;ıkıyorsa sorun yok demektir, devam ediyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d429fda570ce.png" alt="fdisk" border="0" data-featherlight="/images/1/5d429fda570ce.png" /></p>
<p><code>fdisk -l</code> komutu ile t&uuml;m disklerimizi g&ouml;r&uuml;yoruz, biz /dev/sda &uuml;zerine kurulum yapacağız. Dualboot i&ccedil;in yapmanız gereken işlemleri ayrı bir makale olarak yazacağım.</p>
<p>&nbsp;</p>
<p>Diskimizdeki alanları g&ouml;rd&uuml;ğ&uuml;m&uuml;ze g&ouml;re, <code>cfdisk</code> komutu ile diskimizi ayarlamaya başlıyoruz.</p>
<p><img src="/images/1/5d42a0952ce2b.png" alt="cfdisk" border="0" data-featherlight="/images/1/5d42a0952ce2b.png" /></p>
<p>GPT'yi se&ccedil;erek ilerliyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a0ae2c492.png" alt="cfdisk" border="0" data-featherlight="/images/1/5d42a0ae2c492.png" /></p>
<p>İlk &ouml;nce New kısmına gelerek 512M yazıp 512mblik bir EFI/BIOS boot b&ouml;l&uuml;m&uuml; oluşturuyoruz. Oluşturduktan sonra Type kısmına girip sisteminiz EFI ise <strong>EFI System</strong> se&ccedil;eneğini, değilse <strong>BIOS boot</strong> se&ccedil;eneğini se&ccedil;iniz.</p>
<p><strong>*G&uuml;ncelleme 26.03.2020: EFI kurulum yaparken 16M'lik bir BIOS boot b&ouml;l&uuml;m&uuml; ekleyin*</strong></p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a1122421f.png" alt="cfdisk" border="0" data-featherlight="/images/1/5d42a1122421f.png" /></p>
<p>Diğer b&ouml;l&uuml;m&uuml;m&uuml;z&uuml; de istediğimiz boyutta oluşturuyoruz, zaten otomatik olarak Linux filesystem ibaresi gelecektir. Type se&ccedil;memize gerek yok. İşlemi tamamladıktan sonra Write se&ccedil;eneğini se&ccedil;erek diske partition table'ı yazıyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a15ce6f9d.png" alt="fdisk" border="0" data-featherlight="/images/1/5d42a15ce6f9d.png" /></p>
<p>fdisk ile tekrar kontrol ettiğimizde /dev/sda1 i&ccedil;in boot b&ouml;l&uuml;m&uuml;m&uuml;z&uuml;n, /dev/sda2 i&ccedil;in Linux sistemimizin b&ouml;l&uuml;nd&uuml;ğ&uuml;n&uuml; teyit etmiş olduk. Şimdi partitionları formatlamamız gerekiyor. Ben <strong>ext4</strong> tercih ettiğim i&ccedil;in iki partitionu da ext4 olarak bi&ccedil;imlendireceğim.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a1ae269c8.png" alt="mkfs.ext4" border="0" data-featherlight="/images/1/5d42a1ae269c8.png" /></p>
<p><span style="text-decoration: line-through;"><code>mkfs.ext4 /dev/sda1 26.03.2020 itibariyle ge&ccedil;ersiz</code></span></p>
<p><code>mkfs.ext4 /dev/sda2</code></p>
<p><code>mkfs.vfat -F32 /dev/sda1</code></p>
<p>komutlarını kullanarak sistemimizi bi&ccedil;imlendirip mount etmeye hazır hale getirdik.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a212ca950.png" alt="mount" border="0" data-featherlight="/images/1/5d42a212ca950.png" /></p>
<p><code>mount /dev/sda2 /mnt</code></p>
<p><code>mkdir /mnt/boot</code></p>
<p><code>mount /dev/sda1 /mnt/boot</code></p>
<p>komutlarını yazarak işletim sistemi i&ccedil;in gerekli alanları mount ediyoruz. Resimde yanlışlık olmuş, dikkate almayınız.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a26dc88a4.png" alt="pacstrap" border="0" data-featherlight="/images/1/5d42a26dc88a4.png" /></p>
<p><code>pacstrap /mnt base base-devel</code></p>
<p>komutunu kullanarak sistemimizi y&uuml;klemeye başlıyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a2ee16c02.png" alt="fstab" border="0" data-featherlight="/images/1/5d42a2ee16c02.png" /></p>
<p>pacstrap işlemi tamamlandıktan sonra <code>genfstab -U /mnt &gt;&gt; /mnt/etc/fstab</code> komutunu yazarak başlangı&ccedil;ta sistemin mount edilmesini sağlıyoruz. (resimde yanlışlık var)</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a3ac711b4.png" alt="arch-chroot" border="0" data-featherlight="/images/1/5d42a3ac711b4.png" /></p>
<p><code>arch-chroot /mnt</code> komutunu kullanarak kurduğumuz sistemi Live ISO &uuml;zerinde kullanabiliyoruz ve işlemler yapacağız.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a3a4ae513.png" alt="komutlar" border="0" data-featherlight="/images/1/5d42a3a4ae513.png" /></p>
<p><code>ln -sf /usr/share/zoneinfo/Europe/Istanbul /etc/localtime</code></p>
<p><code>hwclock --systohc</code></p>
<p><code>locale-gen</code></p>
<p><code>LANG=tr_TR.UTF-8 &gt;&gt; /etc/locale.conf</code></p>
<p><code>loadkeys trq</code></p>
<p>komutları ile gerekli işlemleri ger&ccedil;ekleştiriyoruz. Temel olarak yerelleştirme işlemlerini yapıyor.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a46a8bf40.png" alt="hostname" border="0" data-featherlight="/images/1/5d42a46a8bf40.png" /></p>
<p><code>nano /etc/hostname</code> komutunu kullanıp i&ccedil;eriye sisteminizin hostnameini yazınız.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a49c9ce98.png" alt="hosts" border="0" data-featherlight="/images/1/5d42a49c9ce98.png" /></p>
<p><code>nano /etc/hosts</code> komutunu kullanıp host dosyamızı a&ccedil;ıyoruz ve i&ccedil;erisine resimde g&ouml;rd&uuml;ğ&uuml;m&uuml;z şeyleri yazıyoruz. <strong>divergent</strong> yerine kendi hostname'inizi yazmanız gerekmektedir.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a4e34067c.png" alt="mkinitcpio -p linux" border="0" data-featherlight="/images/1/5d42a4e34067c.png" /></p>
<p><span style="text-decoration: line-through;"><code>mkinitcpio -p linux</code> komutunu &ccedil;alıştırarak sistemimizi bootable hale getiriyoruz.</span></p>
<p><strong>*G&uuml;ncelleme 26.03.2020:</strong> <code>pacman -S mkinitcpio linux linux-firmware</code> komutunu &ccedil;alıştırarak sistemimizi bootable hale getiriyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a52833826.png" alt="passwd" border="0" data-featherlight="/images/1/5d42a52833826.png" /></p>
<p><code>passwd</code> komutunu yazarak root hesabınız i&ccedil;in şifrenizi belirleyin.</p>
<p>&nbsp;</p>
<p>Yeni kullanıcı hesabı da oluşturmamız gerekiyor. Aşağıdaki komutları girerek oluşturalım.</p>
<ul>
<li><code>nano /etc/sudoers</code> komutunu kullanarak i&ccedil;erideki %wheel ALL=(ALL) ALL g&ouml;rd&uuml;ğ&uuml;m&uuml;z satırın başındaki # işaretini kaldırıyoruz ve kaydediyoruz.</li>
<li><code>useradd -m -G wheel -s /bin/bash KULLANICIADI</code> komutunu kullanarak kullanıcı hesabımızı yaratıyoruz.</li>
<li><code>passwd KULLANICIADI</code> komutu ile kullanıcımıza şifre belirliyoruz.</li>
</ul>
<p>Eğer hata mesajı almadıysak kullanıcımız başarıyla oluşturulmuştur.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a6191e49c.png" alt="grub" border="0" data-featherlight="/images/1/5d42a6191e49c.png" /></p>
<p><code>pacman -S grub</code> komutu ile GRUB bootloaderi kuruyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a64494dc8.png" alt="Grub install" border="0" data-featherlight="/images/1/5d42a64494dc8.png" /></p>
<p><code>grub-install --target=i386-pc /dev/sda</code> komutu ile GRUB bootladerin sistemimize kurulumunu ger&ccedil;ekleştiriyoruz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a67f7581c.png" alt="GRUB mkconfig" border="0" data-featherlight="/images/1/5d42a67f7581c.png" /></p>
<p><code>grub-mkconfig -o /boot/grub/grub.cfg</code> komutu ile GRUB configini yaratıyoruz.</p>
<p><strong>*G&uuml;ncelleme 26.03.2020:</strong> <code>pacman -S dhcpcd</code> komutu ile dhcp servisini kuruyoruz, bir dahaki bootta internete ihtiyacımız olacak.</p>
<p><img src="/images/1/5d42a755dbda0.png" alt="Archlinux grub" border="0" data-featherlight="/images/1/5d42a755dbda0.png" /></p>
<p>Karşımızda Arch Linux! Kurulu bir şekilde hem de.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a78edb776.png" alt="ip link" border="0" data-featherlight="/images/1/5d42a78edb776.png" /></p>
<p>Evet, internetimiz yoktu. Nasıl getireceğiz? Sırasıyla ethernet kablonuz bağlıyken:</p>
<ul>
<li><code>ip link</code></li>
<li><code>dhcpcd</code></li>
</ul>
<p>komutlarını &ccedil;alıştırın ve ping atabildiğinizi g&ouml;receksiniz.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a7db27838.png" alt="git kurulumu" border="0" data-featherlight="/images/1/5d42a7db27838.png" /></p>
<p>AUR helper olan yay yazılımını kurabilmek i&ccedil;in git paketine ihtiyacımız var. <code>pacman -S git</code> komutu ile kurulumu ger&ccedil;ekleştirebilirsiniz ancak sizin sisteme root olarak değil oluşturduğunuz kullanıcı ile giriş yapmanız gerekiyor.</p>
<p>&nbsp;</p>
<p><img src="/images/1/5d42a82720294.png" alt="git clone yay" border="0" data-featherlight="/images/1/5d42a82720294.png" /></p>
<p><code>git clone </code><a href="https://aur.archlinux.org/yay.git"><code>https://aur.archlinux.org/yay.git</code> </a>komutunu kullanarak yay aur helper yazılımını sistemimize kurmak &uuml;zere indiriyoruz, indirme tamamlandıktan sonra <code>cd yay</code> komutunu yazarak dizine ge&ccedil;iyoruz ve <code>makepkg -si</code> komutu ile kuruluma başlıyoruz.</p>
<p>&nbsp;</p>
<p>Sonunda! Kurulum tamamlanmıştır, bundan sonra isterseniz internetten araştırarak KDE, xfce4, deepin gibi DE'lerden birini kurup ilerleyebilirsiniz. Yakında aray&uuml;z kurulumu ve dualboot kurulum i&ccedil;in de rehber yayınlayacağım. Eğer yayınlanırsa buraya linkleri koyacağım, takibi bırakmayın!</p>
<p><strong>D&uuml;zenleme 7 Ağustos 2019</strong>: <a href="/arch-linux-kde-plasma-kurulumu.html">KDE Plasma Kurulumu</a> rehberimiz yayınlanmıştır.</p>
<p>Siz de bir web developersiniz ve Arch &uuml;zerinde nasıl bir LAMP stack kuracağınızı &ouml;ğrenmek istiyorsanız: <a title="Devilbox ile LAMP Stack Kurulumu" href="/devilbox-ile-lamp-stack-kurulumu.html" target="_blank" rel="noopener">Devilbox ile LAMP Stack Kurulumu</a></p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Devilbox ile LAMP Stack Kurulumu]]></title>
            <link>https://dogukan.dev/devilbox-ile-lamp-stack-kurulumu</link>
            <guid>https://dogukan.dev/devilbox-ile-lamp-stack-kurulumu</guid>
            <pubDate>Wed, 31 Jul 2019 15:14:05 GMT</pubDate>
            <description><![CDATA[Devilbox nedir?
Devilbox, Docker Container sistemini kullanarak sisteminize LAMP Stack kurulumu yapmanızı sağlayan bir kurulum paketidir. Kullanımı ve konfigürasyonu kolaydır.

Kurulum tamamlandıktan sonra böyle bir arayüz...]]></description>
            <content:encoded><![CDATA[<p><strong>Devilbox nedir?</strong></p>
<p>Devilbox, Docker Container sistemini kullanarak sisteminize LAMP Stack kurulumu yapmanızı sağlayan bir kurulum paketidir. Kullanımı ve konfig&uuml;rasyonu kolaydır.</p>
<p><img src="/images/1/5d4171f6e3fba.png" alt="Devilbox user interface" border="0" data-featherlight="/images/1/5d4171f6e3fba.png" /></p>
<p>Kurulum tamamlandıktan sonra b&ouml;yle bir aray&uuml;ze sahip olacağız. Kurulum Windows ve Mac i&ccedil;in de mevcutken bug&uuml;n Linux i&ccedil;in anlatacağım.</p>
<p>&Ouml;ncelikle sisteminizde docker ve docker-compose paketlerinin bulunması lazım. Ben Archlinux kullandığımdan (eğer kurmak istiyorsanız <a title="Arch Linux Kurulumu" href="/arch-linux-kurulumu-resimli-anlatim.html" target="_blank" rel="noopener">Arch Linux Kurulumu Resimli Anlatım</a>) buranın paket y&ouml;netim sistemi &uuml;zerinden komutları vereceğim, siz de sisteminize uygun paket y&ouml;neticisi ile bu paketleri kurabilirsiniz.</p>
<p><code>yay -S docker docker-compose</code></p>
<p>komutu ile gerekli paketleri kuruyoruz.</p>
<p>&nbsp;</p>
<p>Sıradaki adım olarak devilbox'u indirmemiz gerekiyor. Bunun i&ccedil;in home dizininize ge&ccedil;iniz (cd ~). Ardından aşağıdaki komut ile devilbox reposunu sistemimize kopyalamamız gerekiyor.</p>
<p><code>git clone https://github.com/cytopia/devilbox</code></p>
<p>&nbsp;</p>
<p>İşlemler tamamlandıktan sonra bulunduğunuz dizinde devilbox isimli bir klas&ouml;r oluşmuş olmalı gerekiyor. Eğer oluştuysa bu dizine ge&ccedil;iş yapıyoruz ve .env dosyamızı oluşturmak i&ccedil;in aşağıdaki komutu yazıyoruz.</p>
<p><code>cp env-example .env</code></p>
<p>&nbsp;</p>
<p>.env dosyamızı d&uuml;zenlemeye başlayabiliriz. Bu dosyada d&uuml;zenlemeniz gereken kritik satırlar:</p>
<ul>
<li>TLD_SUFFIX<br />bind serverin domain uzantılarını belirler, projeadi.SUFFIX şeklinde domainleriniz otomatik oluşacaktır.<br /><br /></li>
<li>TIMEZONE<br />Bu ayarı Europe/Istanbul olarak ayarlamanız gerekmektedir.<br /><br /></li>
<li>DEVILBOX_UI_ENABLE<br />Bu ayarı 1 yapmanız gerekiyor. devilbox adresini size erişilebilir kılacak, resmini eklediğim panele sahip olacak buradan databaselerinizi y&ouml;netebileceksiniz.<br /><br /></li>
<li>İmajlar<br />Burası tamamen sizin stack tercihinize kalmış, baştaki # işaretini kaldırarak se&ccedil;im yapabilir, o containerleri indirmesini sağlayabilirsiniz.<br /><br /></li>
<li>HTTPD Docker Settings<br />Buradan varsayılan docroot ayarlarınızı, portlarınızı vs. değiştirebilirsiniz<br /><br /></li>
<li>MySQL Docker Settings<br />MySQL veritabanınıza şifre ekleyebilirsiniz.</li>
</ul>
<p>&nbsp;</p>
<p>.env dosyamızdaki ayarları tamamladığımıza g&ouml;re artık makineyi &ccedil;alıştırabiliriz. Yapmamız gereken iki işlem var. Docker Containerlerini &ccedil;alıştırmak i&ccedil;in:</p>
<p><code>docker-compose up -d</code></p>
<p>Ardından projelerinizi devilbox/data/www altında klas&ouml;rlendirerek a&ccedil;abilirsiniz. A&ccedil;tığınız klas&ouml;r i&ccedil;erisinde docroot ayarınıza g&ouml;re bir klas&ouml;r a&ccedil;manız daha gerekecektir. (varsayılan olarak public_html)</p>
<p>Klas&ouml;r&uuml;n&uuml;z&uuml; a&ccedil;tıktan sonra &ouml;rneğin eticaret klas&ouml;r&uuml; a&ccedil;tıysanız eticaret.SUFFIX tarzında erişim sağlamak i&ccedil;in sisteminizin host dosyasına 127.0.0.1 eticaret.SUFFIX yazıp kaydetmeniz gerekmektedir.</p>
<p>Index of / yazısını g&ouml;rd&uuml;ğ&uuml;n&uuml;zde sistem tamamen hazır demektir. İyi kullanımlar dilerim :)</p>
<p>Sorularınızı yorum b&ouml;l&uuml;m&uuml;nde belirtirseniz yardımcı olacağım.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
        <item>
            <title><![CDATA[Merhaba dünya!]]></title>
            <link>https://dogukan.dev/merhaba-dunya</link>
            <guid>https://dogukan.dev/merhaba-dunya</guid>
            <pubDate>Tue, 30 Jul 2019 23:44:02 GMT</pubDate>
            <description><![CDATA[
Merhaba dünya!
Tekrardan ben geldim. Evet, uzun yıllar oldu blog yazmayalı... Blog yazmadığım süreç 5 yılı geçti ancak hayatımda hep bir eksiklik hissettim. İnsanlara öğrendiğim şeyleri paylaşacak, bir şeyler öğretecek bir...]]></description>
            <content:encoded><![CDATA[<p><img src="/images/1/5d4097f34eab8.png" alt="Merhaba d&uuml;nya!" width="1000" border="0" /></p>
<p><strong>Merhaba d&uuml;nya!</strong></p>
<p>Tekrardan ben geldim. Evet, uzun yıllar oldu blog yazmayalı... Blog yazmadığım s&uuml;re&ccedil; 5 yılı ge&ccedil;ti ancak hayatımda hep bir eksiklik hissettim. İnsanlara &ouml;ğrendiğim şeyleri paylaşacak, bir şeyler &ouml;ğretecek bir platformun eksikliğini hissettim.</p>
<p>Laravel ile tecr&uuml;be edinmek i&ccedil;in bir i&ccedil;erik y&ouml;netim sistemi geliştirdikten sonra neden yapmıyorum ki dedim kendime. İlk &ouml;nce eski alan adım olan <a href="http://www.dogukanoksuz.com.tr">www.dogukanoksuz.com.tr</a> adresini değiştirmek istedim, blog yazmaya &ccedil;ok uygun değilmiş gibi geldi. İsim arayışında bulunurken kendimi .dev domain uzantısının i&ccedil;erisinde buldum ve ismimin ge&ccedil;tiği <a href="http://www.dogukan.dev">www.dogukan.dev</a> domaininin boş olduğunu g&ouml;rmemle hevesim iyice arttı, projeye d&ouml;rt elle sarılarak hızlıca geliştirdim.</p>
<p><strong>Eee, websitende ne yazmayı planlıyorsun, kişisel bir blog mu olacak?</strong> sorusuna cevabım ise blogumda bilgisayar d&uuml;nyası ile alakalı karşılaştığım sorunların &ccedil;&ouml;z&uuml;mlerinden bahsetmeyi planlıyorum. Genel olarak hangi konular derseniz:</p>
<ul>
<li>Linux</li>
<li>PHP</li>
<li>C++</li>
<li>Python</li>
<li>Windows Sistemler</li>
<li>IT problemleri</li>
</ul>
<p>ve diğer aklıma gelmeyen konular...</p>
<p><strong>Peki bunları yapınca ne elde edeceksin?&nbsp;</strong>sorusunun da y&ouml;neltildiğini hisseder gibiyim, bu sebepten buna da yanıt vereyim. Bir bilgisayar m&uuml;hendisliği &ouml;ğrencisi olarak T&uuml;rk&ccedil;e kaynak bulmakta g&uuml;&ccedil;l&uuml;k &ccedil;ekiyor, b&uuml;t&uuml;n araştırmalarımı İngilizce yapmak zorunda kalıyorum. Bu sebepten &ouml;t&uuml;r&uuml; insanların kolay bilgiye erişimini sağlamak i&ccedil;in yapacağım ancak ufak bir artısı da yok değil. Blog yazarken, insanlara bir şey anlatırken o konu hakkında detaylı bilgiye sahip olacağım ve daha &ccedil;ok araştırmam gerekecek. Bu sayede kendimi daha da geliştirebileceğim. Umarım herkese faydalı olabilirim.</p>
<p>Bu arada projeyi geliştirirken arkamda bulunan abim <strong>Mehmet Akif &Ouml;ks&uuml;z</strong>'e ve <strong>Creentech</strong>'ten <strong>Mert Yakan</strong>'a teşekk&uuml;rlerimi iletiyorum.&nbsp;</p>
<p>Okuyan herkese teşekk&uuml;r ediyor, iyi g&uuml;nler diliyorum.</p>]]></content:encoded>
            <author>me@dogukan.dev (Doğukan Öksüz)</author>
        </item>
    </channel>
</rss>