<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[linu.us Developer blog by Linus Benkner]]></title><description><![CDATA[Hey there, I'm a young developer building software on the web.]]></description><link>https://linu.us</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1649886829718/adr_9x0nu.png</url><title>linu.us Developer blog by Linus Benkner</title><link>https://linu.us</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 11:29:28 GMT</lastBuildDate><atom:link href="https://linu.us/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Exploring Inheritance in PHP]]></title><description><![CDATA[Hello!
Now you know the fundamentals of classes in PHP, it's time to move on to inheritance.
In case you missed the previous posts, you can follow the series here.
What is Inheritance?
The concept of inheritance allows us to create a class that deriv...]]></description><link>https://linu.us/exploring-inheritance-in-php</link><guid isPermaLink="true">https://linu.us/exploring-inheritance-in-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[oop]]></category><category><![CDATA[Object Oriented Programming]]></category><category><![CDATA[inheritance]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Mon, 16 Dec 2024 21:54:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716147548842/d47227fb-fdbe-4cb1-b094-9131dea0ddd0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello!</p>
<p>Now you know the fundamentals of classes in PHP, it's time to move on to inheritance.</p>
<p>In case you missed the previous posts, you can <a target="_blank" href="https://mastering-php.com/">follow the series here</a>.</p>
<h2 id="heading-what-is-inheritance">What is Inheritance?</h2>
<p>The concept of inheritance allows us to create a class that derives from another class. This new class is called a subclass and extends an existing class. For example, we may have a user class and want to add admin users to our app. Instead of creating a whole new AdminUser class with mostly the same functionality, we can instead use inheritance and create a subclass of the User class.</p>
<p>This new subclass inherits all functionality from the User class, but we can override and extend with functionality specific to admin users.</p>
<h2 id="heading-creating-the-superclass">Creating the Superclass</h2>
<p>A superclass is a class that is inherited by subclasses. A superclass can be any class, so let's create one:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name</span>) </span>{
        <span class="hljs-comment">//</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name;
    }
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
<span class="hljs-keyword">echo</span> $user-&gt;getDisplayName(); <span class="hljs-comment">// "John"</span>
</code></pre>
<p>It's a simple user class where each user has a name and a display name that is displayed on the website.</p>
<p>If we want to introduce different types of users, we can create subclasses that inherit, or extend, the user class. We can do so using the <code>extend</code> keyword in the class definition:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{

}

<span class="hljs-comment">// Usage</span>
$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
<span class="hljs-keyword">echo</span> $verified_user-&gt;getDisplayName(); <span class="hljs-comment">// "Jane"</span>
</code></pre>
<p>As you can see, we haven't added anything to the class yet, but the constructor and <code>getDisplayName</code> methods still work! This is because our class inherits the functionality from the superclass, which in this example is the User class.</p>
<h2 id="heading-overriding-methods">Overriding Methods</h2>
<p>We now effectively have two identical classes, which isn't very helpful. However, our verified users should have a cool verified badge next to their name!</p>
<p>To implement this, we can override the existing getDisplayName method (that is currently inherited from the User class) with new functionality:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">" ✔"</span>;
    }
}

<span class="hljs-comment">// Usage</span>
$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
<span class="hljs-keyword">echo</span> $verified_user-&gt;getDisplayName(); <span class="hljs-comment">// "Jane ✔"</span>
</code></pre>
<h3 id="heading-the-override-attribute">The Override Attribute</h3>
<p>Since PHP 8.3, we can explicitly tell PHP that we want to override an existing method using the <code>#[\Override]</code> attribute:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-comment">#[\Override]</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">" ✔"</span>;
    }
}
</code></pre>
<p>In case the function we want to override doesn't exist on the superclass, this will now throw an error:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-comment">#[\Override]</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">" ✔"</span>;
    }
}

<span class="hljs-comment">// Fatal error: VerifiedUser::getName() has #[\Override] attribute,</span>
<span class="hljs-comment">// but no matching parent method exists</span>
</code></pre>
<h2 id="heading-adding-methods">Adding Methods</h2>
<p>Besides overriding existing methods, we can add new methods and properties to our new subclass:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">" ✔"</span>;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isVerifiedSince</span>(<span class="hljs-params"></span>): <span class="hljs-title">DateTime</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DateTime(<span class="hljs-string">"1 month ago"</span>);
    }
}

<span class="hljs-comment">// Usage</span>
$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
<span class="hljs-keyword">echo</span> $verified_user-&gt;isVerifiedSince()-&gt;format(<span class="hljs-string">"Y-m-d"</span>); <span class="hljs-comment">// "2024-04-19"</span>
</code></pre>
<h2 id="heading-composition">Composition</h2>
<p>Now you might ask, why not simply create a new class? Inheritance is cool, but if my class doesn't have that much functionality, can't I simply create a new class from scratch?</p>
<p>And of course, you could, but then you're missing out on a great feature:</p>
<p>Everywhere you can use the User class, you can now also use the VerifiedUser class, since it is a subclass.</p>
<p>This means, let's say we have a function that accepts a User instance:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHelloTo</span>(<span class="hljs-params">User $user</span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello "</span> . $user-&gt;getDisplayName();
}
</code></pre>
<p>This is not limited to the User class, but also accepts subclasses:</p>
<pre><code class="lang-php">$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
sayHelloTo($user); <span class="hljs-comment">// "Hello John"</span>

$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
sayHelloTo($verified_user); <span class="hljs-comment">// "Hello Jane ✔"</span>
</code></pre>
<p>And we didn't have to change the code of the function, it just works!</p>
<p>However, this does not work in the other direction. If we have a function that accepts a VerifiedUser, we can't pass it an instance of the User class:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">wasVerifiedThisYear</span>(<span class="hljs-params">VerifiedUser $user</span>) </span>{
    <span class="hljs-keyword">return</span> $user-&gt;isVerifiedSince()-&gt;format(<span class="hljs-string">"Y"</span>) === date(<span class="hljs-string">"Y"</span>);
}

$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
var_dump( wasVerifiedThisYear($user) );
<span class="hljs-comment">// Fatal error: Uncaught TypeError: wasVerifiedThisYear():</span>
<span class="hljs-comment">// Argument #1 ($user) must be of type VerifiedUser, User given</span>

$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
var_dump( wasVerifiedThisYear($verified_user ) );
<span class="hljs-comment">// bool(true)</span>
</code></pre>
<h2 id="heading-instanceof-operator">Instanceof Operator</h2>
<p>The <code>instanceof</code> operator can be used to check if a given object is an instance of a specific class:</p>
<pre><code class="lang-php">$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);

var_dump( $user <span class="hljs-keyword">instanceof</span> User ); <span class="hljs-comment">// bool(true)</span>
var_dump( $user <span class="hljs-keyword">instanceof</span> VerifiedUser ); <span class="hljs-comment">// bool(false)</span>
</code></pre>
<p>Once we know that an object is an instance of a certain class, we can safely call methods specific to this class:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getVerifiedInfo</span>(<span class="hljs-params">User $user</span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span>(! $user <span class="hljs-keyword">instanceof</span> VerifiedUser) {
        <span class="hljs-keyword">return</span> $user-&gt;getDisplayName() . <span class="hljs-string">" is not verified"</span>;
    }

    <span class="hljs-keyword">return</span> $user-&gt;getDisplayName() . <span class="hljs-string">" is verified since "</span> . $user-&gt;isVerifiedSince()-&gt;format(<span class="hljs-string">"Y-m-d"</span>);
}

$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
<span class="hljs-keyword">echo</span> getVerifiedInfo($user); <span class="hljs-comment">// "John is not verified"</span>

$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
<span class="hljs-keyword">echo</span> getVerifiedInfo($verified_user ); <span class="hljs-comment">// "Jane ✔ is verified since 2024-04-19"</span>
</code></pre>
<h2 id="heading-calling-methods-from-the-superclass">Calling Methods from the Superclass</h2>
<p>Let’s go back to our User example from the beginning:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name</span>) </span>{
        <span class="hljs-comment">//</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name;
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">" ✔"</span>;
    }
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
<span class="hljs-keyword">echo</span> $user-&gt;getDisplayName(); <span class="hljs-comment">// "John"</span>

$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
<span class="hljs-keyword">echo</span> $verified_user-&gt;getDisplayName(); <span class="hljs-comment">// "Jane ✔"</span>
</code></pre>
<p>You can see, the <code>VerifiedUser</code> class overrides the <code>getDisplayName</code> method completely. But in this example, we only want to append a little checkmark symbol to the name.</p>
<p>Instead of copying the logic to get the display name from the superclass <code>User</code>, we can call the method we are overriding by prefixing it with <code>parent::</code>, like this:</p>
<pre><code class="lang-php"> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::getDisplayName() . <span class="hljs-string">" ✔"</span>;
    }
}
</code></pre>
<p>The output is the same as before, but now, if we change how the default display name is generated in the superclass, these changes will also be reflected in the <code>VerfiedUser</code> class:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name</span>) </span>{
        <span class="hljs-comment">//</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-comment"># Let's say we change how the name is displayed:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"@"</span> . strtolower(<span class="hljs-keyword">$this</span>-&gt;name);
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerifiedUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDisplayName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-comment"># We're not changing anything here!</span>
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::getDisplayName() . <span class="hljs-string">" ✔"</span>;
    }
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
<span class="hljs-keyword">echo</span> $user-&gt;getDisplayName(); <span class="hljs-comment">// "@john"</span>

$verified_user = <span class="hljs-keyword">new</span> VerifiedUser(<span class="hljs-string">"Jane"</span>);
<span class="hljs-comment"># Notice how the new format is reflected here, even though</span>
<span class="hljs-comment"># we haven't touched the VerifiedUser class. And the</span>
<span class="hljs-comment"># checkmark is still added!</span>
<span class="hljs-keyword">echo</span> $verified_user-&gt;getDisplayName(); <span class="hljs-comment">// "@jane ✔"</span>
</code></pre>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Extending and overriding methods of existing classes is a very powerful feature. But it’s not only helpful when working with your own classes, it can also come in very handy when you need to change the functionality of a third-party library.</p>
<p>Whenever you’re ready, head over to the next part of this series.</p>
]]></content:encoded></item><item><title><![CDATA[WordPress Is in an Alarming State]]></title><description><![CDATA[Matt Mullenweg wasn't lying when he said he would go "nuclear" against WP Engine. But his attack expanded beyond WP Engine and now hits the entire WordPress ecosystem.
What has started as an attempt to get WP Engine to pay a trademark license to Auto...]]></description><link>https://linu.us/wordpress-is-in-an-alarming-state</link><guid isPermaLink="true">https://linu.us/wordpress-is-in-an-alarming-state</guid><category><![CDATA[WordPress]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[development]]></category><category><![CDATA[cms]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sat, 12 Oct 2024 23:32:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728775791251/06ff47d2-8087-4e22-b9dc-4aad91f4fc0b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Matt Mullenweg wasn't lying when he said he would go "nuclear" against <a target="_blank" href="https://wpengine.com/">WP Engine</a>. But his attack expanded beyond WP Engine and now hits the entire WordPress ecosystem.</p>
<p>What has started as an attempt to get WP Engine to pay a <a target="_blank" href="https://automattic.com/2024/10/01/wpe-terms/">trademark license</a> to <a target="_blank" href="https://automattic.com/">Automattic</a> already resulted in many contributors leaving the project, <a target="_blank" href="https://ma.tt/2024/10/alignment/">Automattic employees resigning</a>, and shattered trust in <a target="_blank" href="https://ma.tt/">Matt Mullenweg</a> and Automattic.</p>
<h1 id="heading-the-scorched-earth-nuclear-approach"><strong>The "scorched earth nuclear approach"</strong></h1>
<p><em>(Yes, he actually wrote that to WP Engine, see the</em> <a target="_blank" href="https://wpengine.com/wp-content/uploads/2024/10/Complaint-WP-Engine-v-Automattic-et-al.pdf"><em>lawsuit on page 24</em></a>)  </p>
<p>Since the beginning of this drama 3 weeks ago when Matt gave <a target="_blank" href="https://www.youtube.com/watch?v=fnI-QcVSwMU">his talk at WordCamp US</a>, things got crazier nearly every day. After <a target="_blank" href="https://wpengine.com/wp-content/uploads/2024/09/Cease-and-Desist-Letter-to-Automattic-and-Request-to-Preserve-Documents-Sent.pdf">cease and desist letters</a> in <a target="_blank" href="https://automattic.com/2024/wp-engine-cease-and-desist.pdf">both directions</a> and <a target="_blank" href="https://wordpress.org/news/2024/09/wp-engine-banned/">blocked access to WordPress.org</a>, I thought we arrived at the peak of this drama when <a target="_blank" href="https://wpengine.com/wp-content/uploads/2024/10/Complaint-WP-Engine-v-Automattic-et-al.pdf">WP Engine filed a lawsuit</a> against Automattic and Matt Mullenweg on October 2nd.</p>
<p>My hope that this public clown show was over quickly vanished, and things were about to take a turn for the worse.</p>
<p><a target="_blank" href="https://x.com/jonoalderson/status/1843985559745921046">More and more people</a>, including <a target="_blank" href="https://x.com/mor10/status/1842640095087919219">core contributors to the project</a>, are getting blocked by WordPress, Matt, and Automattic on X and banned from the official WordPress Slack. This involves <a target="_blank" href="https://x.com/takisbig/status/1843968571296383060">people who organized community events</a>, contributed to core, and more.<br />And the new mandatory "I am not affiliated with WP Engine in any way, financially or otherwise" checkbox on the <a target="_blank" href="https://login.wordpress.org/">WordPress.org login page</a> certainly doesn't help prevent contributors from leaving.</p>
<p><img src="https://world.hey.com/linus.benkner/785f0e56/representations/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHNLd2ZpOHhodSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--89d9f91cca26287f30f2b1729b2c0381112743ce/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDam9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFLQUIya0NBQVU2REhGMVlXeHBkSGxwU3pvTGJHOWhaR1Z5ZXdZNkNYQmhaMlV3T2cxamIyRnNaWE5qWlZRPSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--946116ea0c454412635aa7309bd9472bf633014c/image.png" alt="image.png" /></p>
<p>The WordPress account on X even <a target="_blank" href="https://x.com/WordPress/status/1845121130207535524">makes fun of contributors</a> who can't log in due to this <a target="_blank" href="https://x.com/chribjel/status/1844010621593088315">ridiculous checkbox</a>.</p>
<p><img src="https://world.hey.com/linus.benkner/785f0e56/representations/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHNLd2NWdnhodSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--e5ac6cab82c5debb7fb0aa5363e5bbba1b17a216/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDam9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFLQUIya0NBQVU2REhGMVlXeHBkSGxwU3pvTGJHOWhaR1Z5ZXdZNkNYQmhaMlV3T2cxamIyRnNaWE5qWlZRPSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--946116ea0c454412635aa7309bd9472bf633014c/image.png" alt="image.png" /></p>
<p>What could have been a legal fight between two companies now looks like Matt destroying the open-source project he created.</p>
<p><a target="_blank" href="https://w3techs.com/technologies/overview/content_management">WordPress powers over 40% of the web</a> with a gigantic ecosystem that allows everyone to build a website, from individuals and small businesses to big corporations and <a target="_blank" href="https://wordpress.org/showcase/the-white-house/">The White House</a>.<br />But I don't think this helps much if the people building the software are leaving.</p>
<h1 id="heading-fork-or-takeover"><strong>Fork or Takeover?</strong></h1>
<p>Things got worse yet another time with WordPress "forking" ACF, an extremely popular plugin by WP Engine.<br />But wait, let me say hijacked instead of forked. They didn't create their own plugin, they overtook the existing <a target="_blank" href="https://wordpress.org/plugins/advanced-custom-fields/">ACF plugin on the WordPress.org plugin repository</a> and changed the name and logo. They even kept the download count currently at 2+ million, the <a target="_blank" href="https://wordpress.org/support/plugin/advanced-custom-fields/reviews/">5-star rating</a>, and <a target="_blank" href="https://wordpress.org/support/plugin/advanced-custom-fields/">forum history</a>.</p>
<p><img src="https://world.hey.com/linus.benkner/785f0e56/representations/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHNLd2RYVmhodSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--031b2943e3857df1a4136f047e4f681afe3fe0bf/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDam9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFLQUIya0NBQVU2REhGMVlXeHBkSGxwU3pvTGJHOWhaR1Z5ZXdZNkNYQmhaMlV3T2cxamIyRnNaWE5qWlZRPSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--946116ea0c454412635aa7309bd9472bf633014c/image.png" alt="image.png" /></p>
<p>The screenshot shows the <a target="_blank" href="https://wordpress.org/plugins/advanced-custom-fields/">ACF plugin page on WordPress.org</a> and the <a target="_blank" href="https://plugins.trac.wordpress.org/changeset?sfp_email=&amp;sfph_mail=&amp;reponame=&amp;new=3167679%40advanced-custom-fields&amp;old=3164480%40advanced-custom-fields&amp;sfp_email=&amp;sfph_mail=">commit to the ACF plugin by WordPress.org</a> (capture date: October 12th, 23:33).</p>
<p>And not "just" that—they kept the slug at "advanced-custom-fields" which now means that <a target="_blank" href="https://wordpress.org/news/2024/10/secure-custom-fields/#:~:text=Sites%20that%20continue%20to%20use,Fields%20to%20Secure%20Custom%20Fields.">WordPress sites using the ACF plugin are being automatically updated to the new "SCF" plugin</a> by WordPress.org.</p>
<p>After the SCF announcement, the leader of the Core Fields project, <a target="_blank" href="https://www.scottkclark.com/">Scott Kingsley Clark</a>, stated he was "done making excuses for Matt's actions and will not associate myself with core any longer" (<a target="_blank" href="https://github.com/sc0ttkclark/wordpress-fields-api/blob/0bd85ee6933fd04a8591da3efcd2a75dbd47826f/README.md">source</a>). Yet another contributor who left the project.</p>
<p>This sets a dangerous precedence. Matt locked a competitor out of the official WordPress plugin repository and overtook their plugin, which is used on millions of sites. And because they have been locked out, WP Engine had no chance to reach the 2+ million sites and inform them <a target="_blank" href="https://www.advancedcustomfields.com/blog/installing-and-upgrading-to-the-latest-version-of-acf/">how to update to the new ACF version</a>.</p>
<h1 id="heading-irreversible-damage"><strong>Irreversible Damage</strong></h1>
<p>WordPress is in an alarming state, Matt and Automattic are harming the project, its community, and open-source as a whole.</p>
<p>I think <a target="_blank" href="https://world.hey.com/dhh/automattic-is-doing-open-source-dirty-b95cf128">DHH put it best when he wrote</a>:</p>
<blockquote>
<p>"That's the deal. That's open source. I give you <a target="_blank" href="https://world.hey.com/dhh/the-open-source-gift-exchange-2171e0f0">a gift of code</a>, you accept the terms of the license. There cannot be a second set of shadow obligations that might suddenly apply, if you strike it rich using the software. Then the license is meaningless, the clarity all muddled, and certainty lost."</p>
</blockquote>
<p>This craziness must end immediately. The actions by Matt and Automattic are doing irreversible damage to the WordPress project.<br />What's next? Another plugin takeover? Attacks against another company? More bans and blocks on Slack and X? More contributors leaving?</p>
<p>Is this the end of WordPress as we knew it?</p>
]]></content:encoded></item><item><title><![CDATA[Deep Dive into Classes and Objects in PHP]]></title><description><![CDATA[Never used a class before? Read the introduction to OOP in PHP first.
Hey again!
In the last post, we created our first class and discovered some benefits of using OOP in PHP. However, in this post, we'll dive deeper into classes and what we can do w...]]></description><link>https://linu.us/deep-dive-into-classes-and-objects-in-php</link><guid isPermaLink="true">https://linu.us/deep-dive-into-classes-and-objects-in-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[oop]]></category><category><![CDATA[classes]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Fri, 21 Jun 2024 06:52:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716147390722/bffbd38c-4c70-4ea6-b9a6-d2c6d9dace22.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Never used a class before? Read the</em> <a target="_blank" href="https://linu.us/introduction-to-object-oriented-programming-in-php"><em>introduction to OOP in PHP</em></a> <em>first.</em></p>
<p>Hey again!</p>
<p>In the last post, we created our first class and discovered some benefits of using OOP in PHP. However, in this post, we'll dive deeper into classes and what we can do with them. So, let's get to work!</p>
<h2 id="heading-class-properties-and-methods">Class Properties and Methods</h2>
<p>We'll use the following class as starting point:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{

}
</code></pre>
<h3 id="heading-properties">Properties</h3>
<p>Properties are variables attached to an instance of a class. For example, every user has a name. So, we can add a $name property to the class:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> $name = <span class="hljs-string">"John"</span>;
}
</code></pre>
<p>We can access these properties using the arrow syntax:</p>
<pre><code class="lang-php">$user = <span class="hljs-keyword">new</span> User;
<span class="hljs-keyword">echo</span> $user-&gt;name; <span class="hljs-comment">// "John"</span>
</code></pre>
<p>We can also modify the property:</p>
<pre><code class="lang-php">$user = <span class="hljs-keyword">new</span> User;
$user-&gt;name = <span class="hljs-string">"John Doe"</span>;
<span class="hljs-keyword">echo</span> $user-&gt;name; <span class="hljs-comment">// "John Doe"</span>
</code></pre>
<p>Just like arguments of functions, we can also add type declarations to our properties to ensure type safety:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">declare</span>(strict_types=<span class="hljs-number">1</span>);

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John"</span>;
}
</code></pre>
<p>Now, if we assign a different type than we specified in the class, we will get an error:</p>
<pre><code class="lang-php">$user = <span class="hljs-keyword">new</span> User;
$user-&gt;name = <span class="hljs-number">10</span>;
<span class="hljs-comment">// Fatal error: Uncaught TypeError:</span>
<span class="hljs-comment">// Cannot assign int to property User::$name of type string</span>
</code></pre>
<p>We can also define a property without initialising it:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name;
}
</code></pre>
<p>A common use case is to define properties and then initialise them with values in the class constructor.</p>
<h2 id="heading-methods">Methods</h2>
<p>Methods are functions attached to an instance of a class. Inside a method, we have access to the current instance of the class and can access and modify the properties and call other methods.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello!"</span>;
    }
}

$user = <span class="hljs-keyword">new</span> User;
$user-&gt;greet(); <span class="hljs-comment">// "Hello!"</span>
</code></pre>
<p>The variable <code>$this</code> references the current instance of the class. So if we want to use the user's name in the greeting, we can do so by referencing the <code>$this-&gt;name</code>:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello "</span> . <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">"!"</span>;
    }
}

$user = <span class="hljs-keyword">new</span> User;
$user-&gt;greet(); <span class="hljs-comment">// "Hello John!"</span>
</code></pre>
<p>Like normal functions, methods can also have arguments, type-hinted arguments, and return types:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello "</span> . <span class="hljs-keyword">$this</span>-&gt;name . <span class="hljs-string">"!"</span>;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHello</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name</span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello <span class="hljs-subst">$name</span>"</span>;
    }
}

$user = <span class="hljs-keyword">new</span> User;
$user-&gt;greet(); <span class="hljs-comment">// "Hello John!"</span>

$hello = $user-&gt;sayHello(<span class="hljs-string">"Linus"</span>);
<span class="hljs-keyword">echo</span> $hello; <span class="hljs-comment">// "Hello Linus";</span>
</code></pre>
<h3 id="heading-the-class-constructor">The Class Constructor</h3>
<p>The constructor is a <a target="_blank" href="https://www.php.net/manual/en/language.oop5.magic.php">magic method</a> that gets executed every time a new instance of that class is constructed. For example:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"New user created!"</span>;
    }
}

$user = <span class="hljs-keyword">new</span> User; <span class="hljs-comment">// "New user created!"</span>
</code></pre>
<p>We can also add arguments to the constructor method which then need to be passed when creating a new instance:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name</span>) </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"New user created: "</span> . $name;
    }
}

$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>); <span class="hljs-comment">// "New user created: John"</span>
</code></pre>
<p>But for now, the <code>$name</code> variable is only available in the constructor. But we can define a property on the class and initialise it in the constructor:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;name = $name;
    }
}

$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
<span class="hljs-keyword">echo</span> $user-&gt;name; <span class="hljs-comment">// "John"</span>
</code></pre>
<p>Note that <code>$name</code> refers to the value passed to the constructor, while <code>$this-&gt;name</code> refers to the property <code>$name</code> on the class.</p>
<h3 id="heading-php-8-constructor-property-promotion">PHP 8: Constructor Property Promotion</h3>
<p>The constructor is commonly used to initialise class properties, just like we did above. However, this requires a bunch of code just to assign a variable. Take a look at this:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $firstname;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $lastname;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> $admin;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $firstname, <span class="hljs-keyword">string</span> $lastname, <span class="hljs-keyword">bool</span> $admin</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;firstname = $firstname;
        <span class="hljs-keyword">$this</span>-&gt;lastname = $lastname;
        <span class="hljs-keyword">$this</span>-&gt;admin = $admin;
    }
}
</code></pre>
<p>Thankfully, PHP 8 introduced us to <a target="_blank" href="https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.promotion">constructor property promotion</a>: This allows us to refactor the code above like this:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $firstname,
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $lastname,
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> $admin,
    </span>) </span>{
        <span class="hljs-comment">//</span>
    }
}
</code></pre>
<p>Very cool!</p>
<h3 id="heading-the-class-destructor">The Class Destructor</h3>
<p>There is a constructor that runs when constructing a new instance, so there also is a destructor that runs when an instance is destroyed.</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name,
    </span>) </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Constructed "</span> . <span class="hljs-keyword">$this</span>-&gt;name;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__destruct</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Destructing "</span> . <span class="hljs-keyword">$this</span>-&gt;name;
    }
}

$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John"</span>);
<span class="hljs-comment">// "Constructed John"</span>

<span class="hljs-keyword">unset</span>($user);
<span class="hljs-comment">// "Destructing John"</span>
</code></pre>
<h3 id="heading-more-magic-methods">More Magic Methods</h3>
<p>There are a lot more magic methods that we haven't covered yet, like clone, get, set, <a target="_blank" href="https://www.php.net/manual/en/language.oop5.magic.php">and more</a>.</p>
<p>If you stumble over a method starting with "__" (two underscores), you just found a magic method. They are reserved to PHP (so don't create your own), but you can implement them, just like we did with the constructor and destructor.</p>
<h2 id="heading-visibility-public-private-and-protected">Visibility: Public, Private, and Protected</h2>
<p>You probably have noticed that we added a <code>public</code> in front of all properties and methods we added to our classes to far. But what does it actually mean?</p>
<p><code>public</code>, <code>private</code>, and <code>protected</code> define the visibility, or in other words, where the property/method can be accessed from:</p>
<h3 id="heading-public">Public</h3>
<p>Public means just what it says: It's public, so you can access it from both inside and outside the class:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;doSomething(); <span class="hljs-comment">// ✅ Works (call method from inside the class)</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doSomething</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> </span>{
        $name = <span class="hljs-keyword">$this</span>-&gt;name; <span class="hljs-comment">// ✅ Works (use property from inside the class)</span>
    }
}

$user = <span class="hljs-keyword">new</span> User;
<span class="hljs-keyword">echo</span> $user-&gt;name; <span class="hljs-comment">// ✅ Works (use property from outside the class)</span>

<span class="hljs-keyword">echo</span> $user-&gt;doSomething(); <span class="hljs-comment">// ✅ Works (call method from outside the class)</span>
</code></pre>
<h3 id="heading-private">Private</h3>
<p>Private means the opposite: It can only be accessed from inside the class and not the outside:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;doSomething(); <span class="hljs-comment">// ✅ Works (call method from inside the class)</span>
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doSomething</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> </span>{
        $name = <span class="hljs-keyword">$this</span>-&gt;name; <span class="hljs-comment">// ✅ Works (use property from inside the class)</span>
    }
}

$user = <span class="hljs-keyword">new</span> User;
<span class="hljs-keyword">echo</span> $user-&gt;name; <span class="hljs-comment">// ❌ Fails (use property from outside the class)</span>

<span class="hljs-keyword">echo</span> $user-&gt;doSomething(); <span class="hljs-comment">// ❌ Fails (call method from outside the class)</span>
</code></pre>
<h3 id="heading-protected">Protected</h3>
<p>Just like private, protected means you can't access the property/method from outside the class, but you can from the inside.</p>
<p>To understand the difference, we need an example with a class that extends another class:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name <span class="hljs-string">"John"</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $password = <span class="hljs-string">"12345"</span>;
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">int</span> $balance = <span class="hljs-number">200</span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SuperUser</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doSomething</span>(<span class="hljs-params"></span>) </span>{
        $name = <span class="hljs-keyword">$this</span>-&gt;name; <span class="hljs-comment">// ✅ Works</span>
        $password = <span class="hljs-keyword">$this</span>-&gt;password; <span class="hljs-comment">// ❌ Fails</span>
        $balance = <span class="hljs-keyword">$this</span>-&gt;balance; <span class="hljs-comment">// ✅ Works</span>
    }
}

$superuser = <span class="hljs-keyword">new</span> SuperUser;
$name = $superuser-&gt;name; <span class="hljs-comment">// ✅ Works</span>
$password = $superuser-&gt;password; <span class="hljs-comment">// ❌ Fails</span>
$balance = $superuser-&gt;balance; <span class="hljs-comment">// ❌ Fails</span>
</code></pre>
<p>As you might can see, the difference lies in the visibility to the subclass. If the User class defines a property that is protected, it can't be accessed from the outside, but a class that extends the User class can access it.</p>
<p>If a property is private, it is only accessible from inside that class, and cannot be accessed from the outside or a class that extends it.</p>
<p><em>Don't worry if you don't yet understand how extends work, we'll take about that later in this series!</em></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Visibility</td><td>Visible to own class</td><td>Visible to subclass</td><td>Visible to the outside</td></tr>
</thead>
<tbody>
<tr>
<td>public</td><td>✅</td><td>✅</td><td>✅</td></tr>
<tr>
<td>protected</td><td>✅</td><td>✅</td><td>❌</td></tr>
<tr>
<td>private</td><td>✅</td><td>❌</td><td>❌</td></tr>
</tbody>
</table>
</div><p>Note: If you don't define the visibility, it will automatically be public.</p>
<h2 id="heading-getters-and-setters">Getters and Setters</h2>
<p>Now we know about properties, methods, and visibility, we can talk about getters and setters.</p>
<p>Getters and setters are functions that allow you to get and set something:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $name;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name</span>): <span class="hljs-title">void</span> </span>{
        <span class="hljs-keyword">$this</span>-&gt;name = $name;
    }
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User;
$user-&gt;setName(<span class="hljs-string">"John"</span>);
<span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello "</span> . $user-&gt;getName(); <span class="hljs-comment">// Hello John</span>
</code></pre>
<p>As you can see, since we now have two public methods to get and set the name, we can change the visibility of the <code>$name</code> property to private.</p>
<p>This in itself might not seem too helpful, but this enables us a few things:</p>
<h3 id="heading-validation">Validation</h3>
<p>We can add validation to the setter:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $name;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;name;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name</span>): <span class="hljs-title">void</span> </span>{
        <span class="hljs-keyword">if</span>(strlen($name) &lt; <span class="hljs-number">2</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Exception</span>(<span class="hljs-string">"Name must be at least 2 characters long"</span>);
        }

        <span class="hljs-keyword">$this</span>-&gt;name = $name;
    }
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User;
$user-&gt;setName(<span class="hljs-string">"J"</span>); <span class="hljs-comment">// ❌ Exception: "Name must be at least 2 characters long"</span>
$user-&gt;setName(<span class="hljs-string">"John"</span>); <span class="hljs-comment">// ✅ Works</span>
</code></pre>
<h3 id="heading-hide-internal-representation-of-the-data">Hide Internal Representation of the Data</h3>
<p>Let's say we have a first and last name. The getter can return the full name, while the data is actually stored in two different properties:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $firstname;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> $lastname;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setFirstName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $firstname</span>): <span class="hljs-title">void</span> </span>{
        <span class="hljs-keyword">$this</span>-&gt;firstname = $firstname;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setLastName</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $lastname</span>): <span class="hljs-title">void</span> </span>{
        <span class="hljs-keyword">$this</span>-&gt;lastname = $lastname;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFullName</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;firstname . <span class="hljs-string">' '</span> . <span class="hljs-keyword">$this</span>-&gt;lastname;
    }
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User;
$user-&gt;setFirstName(<span class="hljs-string">"John"</span>);
$user-&gt;setLastName(<span class="hljs-string">"Doe"</span>);
$fullname = $user-&gt;getFullName(); <span class="hljs-comment">// "John Doe"</span>
</code></pre>
<h2 id="heading-next-steps">Next Steps</h2>
<p>To recap, in this post you have learned:</p>
<ul>
<li><p>How to create a class</p>
</li>
<li><p>How to define and use properties and methods</p>
</li>
<li><p>What the constructor is (and the destructor)</p>
</li>
<li><p>The different visibilities (public, private, protected)</p>
</li>
<li><p>Getters and setters</p>
</li>
</ul>
<p>In the next part of this series, we'll dive into inheritance and the <code>extends</code> keyword (you have already seen an example in this post!)</p>
<p>But feel free to play around with classes before continuing. Understanding how classes work is fundamental for this series.</p>
<p>Whenever you feel ready, continue to the next part about <a target="_blank" href="https://linu.us/exploring-inheritance-in-php">inheritance</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Introduction to Object-Oriented Programming (OOP) in PHP]]></title><description><![CDATA[Hey!
This is the first of a series where we transform simple scripts into powerful, modular applications! If you've been coding in PHP but haven't explored the magic of Object-Oriented Programming (OOP) yet, you're in for a treat. Starting with the b...]]></description><link>https://linu.us/introduction-to-object-oriented-programming-in-php</link><guid isPermaLink="true">https://linu.us/introduction-to-object-oriented-programming-in-php</guid><category><![CDATA[PHP]]></category><category><![CDATA[Object Oriented Programming]]></category><category><![CDATA[design patterns]]></category><category><![CDATA[php8]]></category><category><![CDATA[oop]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Wed, 12 Jun 2024 17:39:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716147347718/0183777f-67df-47d5-994c-9ec6224654de.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey!</p>
<p>This is the first of a series where we transform simple scripts into powerful, modular applications! If you've been coding in PHP but haven't explored the magic of Object-Oriented Programming (OOP) yet, you're in for a treat. Starting with the basics (what is a class, anyway?) we will make our way up to more advanced topics like dependency injection, service containers, and different design patterns.</p>
<p>We'll start by breaking down the fundamental concepts of OOP — what it is, why it matters, and how it can revolutionize your approach to coding. Through simple, hands-on examples, you'll see how classes and objects form the backbone of this paradigm, setting the stage for more complex and exciting topics in our upcoming posts.</p>
<p>Ready to unlock a new level of coding proficiency? Let's dive into the basics of OOP in PHP and <strong>discover how to build better, cleaner, and more scalable applications</strong> from the ground up!</p>
<hr />
<h2 id="heading-what-is-object-oriented-programming-oop">What is Object-Oriented Programming (OOP)?</h2>
<blockquote>
<p>In OOP, computer programs are designed by making them out of objects that interact with one another. — <a target="_blank" href="https://en.wikipedia.org/wiki/Object-oriented_programming">Wikipedia</a></p>
</blockquote>
<p>In the OOP world, you're mostly working with classes. You have one class that interacts with another class, which interacts with another class, and so on.</p>
<p>But before we go too deep into the concepts behind OOP, let's start with some code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

$user = <span class="hljs-string">"John Doe"</span>;
$balance = <span class="hljs-number">100</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
    <span class="hljs-keyword">global</span> $balance;
    $balance += $amount;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
    <span class="hljs-keyword">global</span> $balance;
    $balance -= $amount;
}

<span class="hljs-comment">// Usage</span>
deposit(<span class="hljs-number">500</span>);
withdraw(<span class="hljs-number">200</span>);

<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance: $"</span> . $balance;
<span class="hljs-comment">// Balance: $400</span>
</code></pre>
<p>As you can see, we define a user, a balance, and two functions to modify it.</p>
<p><em>Tip: You can play around with the examples in this post on the</em> <a target="_blank" href="https://onlinephp.io/"><em>online php playground</em></a><em>!</em></p>
<p>This works for now, but there's a good chance we'll run into issues with this. For example, this only works with one <code>$balance</code> variable. What if we have a transaction and want to withdraw from one account and deposit to another?</p>
<h2 id="heading-writing-the-first-class">Writing the First Class</h2>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">In this post, we'll quickly go over how to create a class to understand how it can improve our code. In the next part of this series, we will take a look at this again and go over it in detail, step by step.</div>
</div>

<p>Now, let's rebuild it with OOP in mind. To get started, we can think about the main objects we are working with. In this example, that's the user. So, let's create a user class!</p>
<p>But what is a class, anyway? A class is an object that has properties (attributes) and methods (functions). And, you can have many instances of one class.</p>
<p>In PHP, we define a class using the class keyword followed by a name. The name should be in PascalCase:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{

}
</code></pre>
<p>Cool! We can now create new instances of that class using the <code>new</code> keyword:</p>
<pre><code class="lang-php">$user1 = <span class="hljs-keyword">new</span> User;
var_dump($user1);

$user2 = <span class="hljs-keyword">new</span> User;
var_dump($user2);
</code></pre>
<p>The result looks like this:</p>
<pre><code class="lang-php"><span class="hljs-keyword">object</span>(User)<span class="hljs-comment">#1 (0) {</span>
}
<span class="hljs-keyword">object</span>(User)<span class="hljs-comment">#2 (0) {</span>
}
</code></pre>
<p>The different IDs behind the class name indicate that both objects are different instances of that class. If you <code>var_dump</code> the <code>$user1</code> variable twice, the ID will be the same, as it's the same instance:</p>
<pre><code class="lang-php">$user1 = <span class="hljs-keyword">new</span> User;
var_dump($user1); <span class="hljs-comment">// object(User)#1 (0) { }</span>
var_dump($user1); <span class="hljs-comment">// object(User)#1 (0) { }</span>
</code></pre>
<h3 id="heading-adding-properties">Adding Properties</h3>
<p>But our class is still empty, so let's change that. We can define properties directly inside the class:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name = <span class="hljs-string">"John Doe"</span>;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $balance = <span class="hljs-number">100</span>;
}
</code></pre>
<p><em>Note: We'll come back later to</em><code>public</code><em>. For now, just remember that public means it can be accessed from outside the class.</em></p>
<p>If we <code>var_dump</code> our two user instances again, we can see the following output:</p>
<pre><code class="lang-php"><span class="hljs-keyword">object</span>(User)<span class="hljs-comment">#1 (2) {</span>
  [<span class="hljs-string">"name"</span>]=&gt; <span class="hljs-keyword">string</span>(<span class="hljs-number">8</span>) <span class="hljs-string">"John Doe"</span>
  [<span class="hljs-string">"balance"</span>]=&gt; <span class="hljs-keyword">int</span>(<span class="hljs-number">100</span>)
}
<span class="hljs-keyword">object</span>(User)<span class="hljs-comment">#2 (2) {</span>
  [<span class="hljs-string">"name"</span>]=&gt; <span class="hljs-keyword">string</span>(<span class="hljs-number">8</span>) <span class="hljs-string">"John Doe"</span>
  [<span class="hljs-string">"balance"</span>]=&gt; <span class="hljs-keyword">int</span>(<span class="hljs-number">100</span>)
}
</code></pre>
<h3 id="heading-the-class-constructor">The Class Constructor</h3>
<p>Both instances have the same values since we hardcoded them in the class. Let's change that and instead assign them when creating an instance. To do this, we need to add a class constructor. The constructor is a method that is called when creating a new instance of the class, and it can take parameters just like normal functions:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $balance;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name, <span class="hljs-keyword">int</span> $balance</span>) </span>{

    }
}
</code></pre>
<p>Now, the only thing missing is to assign the parameters received from the constructor to the class properties. We can reference properties and methods inside the class using the <code>this</code> keyword:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $balance;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name, <span class="hljs-keyword">int</span> $balance</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;name = $name;
        <span class="hljs-keyword">$this</span>-&gt;balance = $balance;
    }
}
</code></pre>
<p>Now, we can pass in the data when creating instances of the User class:</p>
<pre><code class="lang-php">$user1 = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John Doe"</span>, <span class="hljs-number">100</span>);
var_dump($user1);

$user2 = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"Jane Doe"</span>, <span class="hljs-number">200</span>);
var_dump($user2);
</code></pre>
<p>This results in the following output:</p>
<pre><code class="lang-php"><span class="hljs-keyword">object</span>(User)<span class="hljs-comment">#1 (2) {</span>
  [<span class="hljs-string">"name"</span>]=&gt; <span class="hljs-keyword">string</span>(<span class="hljs-number">8</span>) <span class="hljs-string">"John Doe"</span>
  [<span class="hljs-string">"balance"</span>]=&gt; <span class="hljs-keyword">int</span>(<span class="hljs-number">100</span>)
}
<span class="hljs-keyword">object</span>(User)<span class="hljs-comment">#2 (2) {</span>
  [<span class="hljs-string">"name"</span>]=&gt; <span class="hljs-keyword">string</span>(<span class="hljs-number">8</span>) <span class="hljs-string">"Jane Doe"</span>
  [<span class="hljs-string">"balance"</span>]=&gt; <span class="hljs-keyword">int</span>(<span class="hljs-number">200</span>)
}
</code></pre>
<h3 id="heading-using-class-properties">Using Class Properties</h3>
<p>We can also use the class properties:</p>
<pre><code class="lang-php">$user1-&gt;balance += <span class="hljs-number">300</span>;
<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance of "</span> . $user1-&gt;name . <span class="hljs-string">": "</span> . $user1-&gt;balance;
<span class="hljs-comment">// Balance of John Doe: 400</span>

<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance of "</span> . $user2-&gt;name . <span class="hljs-string">": "</span> . $user2-&gt;balance;
<span class="hljs-comment">// Balance of Jane Doe: 200</span>
</code></pre>
<h3 id="heading-adding-methods">Adding Methods</h3>
<p>To fully convert our previous example to a class, we also need to add two methods to deposit and withdraw some amount from the balance. We can do so by adding public functions to the class (and functions inside a class are called methods).</p>
<p>These methods can also reference properties and other methods in the class using <code>$this</code>:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $balance;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name, <span class="hljs-keyword">int</span> $balance</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;name = $name;
        <span class="hljs-keyword">$this</span>-&gt;balance = $balance;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;balance += $amount;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;balance -= $amount;
    }
}
</code></pre>
<p>And we can use these methods like this:</p>
<pre><code class="lang-php">$user1 = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John Doe"</span>, <span class="hljs-number">100</span>);
$user1-&gt;deposit(<span class="hljs-number">500</span>);
<span class="hljs-keyword">echo</span> $user1-&gt;name . <span class="hljs-string">"'s balance: "</span> . $user1-&gt;balance;
<span class="hljs-comment">// John Doe's balance: 600</span>

$user2 = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"Jane Doe"</span>, <span class="hljs-number">200</span>);
$user2-&gt;withdraw(<span class="hljs-number">100</span>);
<span class="hljs-keyword">echo</span> $user2-&gt;name . <span class="hljs-string">"'s balance: "</span> . $user2-&gt;balance;
<span class="hljs-comment">// Jane Doe's balance: 100</span>
</code></pre>
<p>To wrap it up, we converted the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

$user = <span class="hljs-string">"John Doe"</span>;
$balance = <span class="hljs-number">100</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
    <span class="hljs-keyword">global</span> $balance;
    $balance += $amount;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
    <span class="hljs-keyword">global</span> $balance;
    $balance -= $amount;
}

<span class="hljs-comment">// Usage</span>
deposit(<span class="hljs-number">500</span>);
withdraw(<span class="hljs-number">200</span>);

<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance: $"</span> . $balance;
<span class="hljs-comment">// Balance: $400</span>
</code></pre>
<p>To this:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> $name;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $balance;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name, <span class="hljs-keyword">int</span> $balance</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;name = $name;
        <span class="hljs-keyword">$this</span>-&gt;balance = $balance;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;balance += $amount;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $amount</span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;balance -= $amount;
    }
}

$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John Doe"</span>, <span class="hljs-number">100</span>);

$user-&gt;deposit(<span class="hljs-number">500</span>);
$user-&gt;withdraw(<span class="hljs-number">200</span>);

<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance: $"</span> . $user-&gt;balance;
<span class="hljs-comment">// Balance: $400</span>
</code></pre>
<h3 id="heading-interact-with-another-class">Interact with Another Class</h3>
<p>But with the introduction of the user class, we are no longer limited to a global balance variable and one user but instead can create multiple users where each user has their own balance.</p>
<p>We can also extend this by allowing one user to pay another user:</p>
<pre><code class="lang-php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    ...

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pay</span>(<span class="hljs-params">User $payee, <span class="hljs-keyword">int</span> $amount</span>) </span>{
        <span class="hljs-keyword">if</span>(<span class="hljs-keyword">$this</span>-&gt;balance &lt; $amount) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Exception</span>(<span class="hljs-string">"Insufficient funds!"</span>);
        }

        <span class="hljs-keyword">$this</span>-&gt;withdraw($amount);
        $payee-&gt;deposit($amount);
    }

    ...
}

<span class="hljs-comment">// Usage</span>
$user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"John Doe"</span>, <span class="hljs-number">100</span>);
$user2 = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"Jane Doe"</span>, <span class="hljs-number">200</span>);

$user-&gt;pay($user2, <span class="hljs-number">50</span>);

<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance of "</span> . $user-&gt;name . <span class="hljs-string">": $"</span> . $user-&gt;balance;
<span class="hljs-comment">// Balance of John Doe: $50</span>

<span class="hljs-keyword">echo</span> <span class="hljs-string">"Balance of "</span> . $user2-&gt;name . <span class="hljs-string">": $"</span> . $user2-&gt;balance;
<span class="hljs-comment">// Balance of Jane Doe: $250</span>
</code></pre>
<h2 id="heading-benefits-of-using-oop-in-php">Benefits of Using OOP in PHP</h2>
<p>What's great about our new User class is the grouping of all related functionality inside this one class. There are no global variables, no functions spread across the codebase, and all data and functions are inside the class.</p>
<p>If you are using the User class and want to know what you can do with it, you can find all available methods in this one place.</p>
<p>As a bonus, most IDEs can show you the available methods while typing a variable that is a User instance:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1716132546906/6c997f9b-a2cf-4e14-af84-96437d3e34d4.png" alt class="image--center mx-auto" /></p>
<p>Also, with this class structure, our application is very modular. Imagine your database connection, logger, and cache separate classes. For example, if you need to change the logger to use Redis, you only need to change the logger class, because all logic related to the cache is stored right in there.</p>
<p>And if you need to create for example an admin user type, you can create a new class that extends the user class and just adds the functionality for the admin. No need to re-implement the user functionality, it's inherited from the base class (in this case the user class).</p>
<p>But don't worry if you are not familiar with stuff like inheritance, we'll get to that!</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>If this was your first time diving into the world of OOP, this was a lot of stuff! Play around with the examples in this post, add some methods, and maybe even create another class!</p>
<p>When you're ready, head over to the next post.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">You can find the next posts of this series here: <a target="_blank" href="https://mastering-php.com/">https://mastering-php.com/</a></div>
</div>]]></content:encoded></item><item><title><![CDATA[How to Create QR Codes In Laravel 11]]></title><description><![CDATA[Interested in adding QR codes to your Laravel 11 project? This guide will show you how! Let's walk through the steps together to easily integrate QR code generation into your Laravel 11 application.
Setting up Laravel 11
If you don't already have a L...]]></description><link>https://linu.us/how-to-create-qr-codes-using-laravel-11</link><guid isPermaLink="true">https://linu.us/how-to-create-qr-codes-using-laravel-11</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><category><![CDATA[development]]></category><category><![CDATA[backend]]></category><category><![CDATA[images]]></category><category><![CDATA[Qrcode]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sun, 28 Apr 2024 18:00:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714326555028/05d0605b-87ed-43de-832c-758d80ae9dc3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Interested in adding QR codes to your Laravel 11 project? This guide will show you how! Let's walk through the steps together to easily integrate QR code generation into your Laravel 11 application.</p>
<h2 id="heading-setting-up-laravel-11">Setting up Laravel 11</h2>
<p>If you don't already have a Laravel application set up, you can simply create a new one by following the <a target="_blank" href="https://laravel.com/docs/11.x#creating-a-laravel-project">installation guide in the docs</a> or simply running the following command:</p>
<pre><code class="lang-bash">composer create-project laravel/laravel example-app
</code></pre>
<p>Once installed, navigate into the <code>example-app</code> directory and start the Laravel dev server:</p>
<pre><code class="lang-bash">php artisan serve
</code></pre>
<h2 id="heading-the-laravel-qr-code-package">The Laravel QR Code Package</h2>
<p>There is a cool package by <a target="_blank" href="https://github.com/giauphan">GiauPhan</a> called <a target="_blank" href="https://packagist.org/packages/giauphan/laravel-qr-code">Laravel QR Code</a> that provides an easy way to generate QR codes in a Laravel application.</p>
<p>To get started, install the package using composer:</p>
<pre><code class="lang-bash">composer require giauphan/laravel-qr-code
</code></pre>
<p>This package provides a <code>QRCode</code> <a target="_blank" href="https://laravel.com/docs/11.x/facades#how-facades-work">facade</a> that we can use to create QR codes. So, let's get to work!</p>
<h2 id="heading-prepare-a-route-for-testing">Prepare a Route for Testing</h2>
<p>If you don't already have a spot where you need to create a QR code and want to play around with the package, you can create a testing route.</p>
<p>Let's define a route in the <code>routes/web.php</code> file and call it <code>qr-code</code>:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">"/qr-code"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//</span>
});
</code></pre>
<p>Inside the route handler function you can now put the code we're about to write to create QR codes.</p>
<h2 id="heading-how-to-use-the-qr-code-package">How to Use the QR Code Package</h2>
<p>The facade provided by the package provides multiple methods to create different types of QR codes:</p>
<ul>
<li><p>Email message</p>
</li>
<li><p>Phone number</p>
</li>
<li><p>SMS</p>
</li>
<li><p>Text</p>
</li>
<li><p>URL</p>
</li>
<li><p>MeCard (contact)</p>
</li>
<li><p>vCard (contact)</p>
</li>
<li><p>WiFi network settings</p>
</li>
</ul>
<p>For example, to create a QR code that contains some text, there is the <code>text</code> method:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::text(<span class="hljs-string">"Hello World"</span>);
</code></pre>
<p>To directly output the QR code, use the <code>png</code> or <code>svg</code> methods:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

<span class="hljs-comment">// PNG</span>
QRCode::text(<span class="hljs-string">"Hello World"</span>)-&gt;png();

<span class="hljs-comment">// SVG</span>
QRCode::text(<span class="hljs-string">"Hello World"</span>)-&gt;svg();
</code></pre>
<p>We'll dive into other QR code types like URL and MeCard in just a second, but first let's take a look what else we can do with the package.</p>
<h2 id="heading-storing-qr-codes-as-images">Storing QR Codes as Images</h2>
<p>A generated QR code can easily be saved on the filesystem using the <code>setOutfile</code> method. It accepts a path to save the file to which we can get using the <a target="_blank" href="https://laravel.com/docs/11.x/filesystem">Laravel storage facade</a>:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Storage</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::text(<span class="hljs-string">"Hello World"</span>)
    -&gt;setOutfile(Storage::disk(<span class="hljs-string">"public"</span>)-&gt;path(<span class="hljs-string">"qrcode.png"</span>))
    -&gt;png();
</code></pre>
<p>With default Laravel configuration, this will save the file to <code>&lt;project-directory&gt;/storage/app/public/qrcode.png</code>. This works with the <code>svg</code> method too.</p>
<h2 id="heading-customizing-the-image">Customizing the Image</h2>
<h3 id="heading-changing-the-size">Changing the Size</h3>
<p>By default, the pixel size is set to <code>4</code>. You can change it to a value from <code>1</code> to <code>10</code> to make it bigger or smaller using the <code>setSize</code> method:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::text(<span class="hljs-string">"Hello World"</span>)
    -&gt;setSize(<span class="hljs-number">10</span>)
    -&gt;png();
</code></pre>
<h3 id="heading-changing-the-margin">Changing the Margin</h3>
<p>The margin is set <code>3</code> by default, but you can change it to any size from <code>1</code> to <code>10</code> or remove it completely by setting it to <code>0</code>:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::text(<span class="hljs-string">"Hello World"</span>)
    -&gt;setMargin(<span class="hljs-number">2</span>)
    -&gt;png();
</code></pre>
<h2 id="heading-setting-the-error-correction-level">Setting the Error Correction Level</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Level</td><td>Value</td><td>Bytes that can be restored</td></tr>
</thead>
<tbody>
<tr>
<td>Low</td><td><code>L</code></td><td>7%</td></tr>
<tr>
<td>Medium</td><td><code>M</code></td><td>15%</td></tr>
<tr>
<td>Quartile</td><td><code>Q</code></td><td>25%</td></tr>
<tr>
<td>High</td><td><code>H</code></td><td>30%</td></tr>
</tbody>
</table>
</div><p>The <a target="_blank" href="https://en.wikipedia.org/wiki/QR_code#Error_correction">error correction level</a> can be set via the <code>setErrorCorrectionLevel</code> method:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::text(<span class="hljs-string">"Hello World"</span>)
    -&gt;setErrorCorrectionLevel(<span class="hljs-string">"Q"</span>)
    -&gt;png();
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">When chaining multiple methods together, <strong>make sure the </strong><code>png</code><strong> or </strong><code>svg</code><strong> methods are called last</strong>. Both methods will save or output the QR code, so apply all required settings before.</div>
</div>

<h2 id="heading-more-qr-code-types">More QR Code Types</h2>
<h3 id="heading-url">URL</h3>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::url(<span class="hljs-string">"https://linu.us"</span>)
    -&gt;png();
</code></pre>
<h3 id="heading-sms">SMS</h3>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::sms(<span class="hljs-string">'+55 (31) 1234-5678'</span>, <span class="hljs-string">'Text to send!'</span>)
    -&gt;png();
</code></pre>
<h3 id="heading-phone-number">Phone Number</h3>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

QRCode::phone(<span class="hljs-string">'+55 31 1234-5678'</span>)
    -&gt;png();
</code></pre>
<h3 id="heading-email-message">Email Message</h3>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

$to = <span class="hljs-string">'john.doe@example.com'</span>;
$subject = <span class="hljs-string">'QR Code Message'</span>;
$body = <span class="hljs-string">'This email was created from a QR Code!'</span>;

QRCode::email($to, $body, $subject)
    -&gt;png();
</code></pre>
<h3 id="heading-mecard">MeCard</h3>
<p>A <a target="_blank" href="https://en.wikipedia.org/wiki/MeCard_(QR_code)">MeCard</a> stores information about a single contact.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

$name = <span class="hljs-string">'John Doe'</span>;
$street = <span class="hljs-string">'1234 Main st.'</span>;
$phone = <span class="hljs-string">'+1 001 555-1234'</span>;
$email = <span class="hljs-string">'john.doe@example.com'</span>;

QRCode::meCard($name, $street, $phone, $email)
    -&gt;png();
</code></pre>
<h3 id="heading-vcard">vCard</h3>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">LaravelQRCode</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">QRCode</span>;

<span class="hljs-comment">// Personal Information</span>
$firstName = <span class="hljs-string">'John'</span>;
$lastName = <span class="hljs-string">'Doe'</span>;
$title = <span class="hljs-string">'Mr.'</span>;
$email = <span class="hljs-string">'john.doe@example.com'</span>;
$company = <span class="hljs-string">"Acme Inc."</span>;
$job = <span class="hljs-string">"Developer"</span>;
$url = <span class="hljs-string">"https://example.com"</span>;

<span class="hljs-comment">// Addresses</span>
$homeAddress = [
    <span class="hljs-string">'type'</span> =&gt; <span class="hljs-string">'home'</span>,
    <span class="hljs-string">'pref'</span> =&gt; <span class="hljs-literal">true</span>,
    <span class="hljs-string">'street'</span> =&gt; <span class="hljs-string">'123 my street st'</span>,
    <span class="hljs-string">'city'</span> =&gt; <span class="hljs-string">'My Beautiful Town'</span>,
    <span class="hljs-string">'state'</span> =&gt; <span class="hljs-string">'LV'</span>,
    <span class="hljs-string">'country'</span> =&gt; <span class="hljs-string">'Neverland'</span>,
    <span class="hljs-string">'zip'</span> =&gt; <span class="hljs-string">'12345-678'</span>
];
$wordAddress = [
    <span class="hljs-string">'type'</span> =&gt; <span class="hljs-string">'work'</span>,
    <span class="hljs-string">'pref'</span> =&gt; <span class="hljs-literal">false</span>,
    <span class="hljs-string">'street'</span> =&gt; <span class="hljs-string">'123 my work street st'</span>,
    <span class="hljs-string">'city'</span> =&gt; <span class="hljs-string">'My Dreadful Town'</span>,
    <span class="hljs-string">'state'</span> =&gt; <span class="hljs-string">'LV'</span>,
    <span class="hljs-string">'country'</span> =&gt; <span class="hljs-string">'Hell'</span>,
    <span class="hljs-string">'zip'</span> =&gt; <span class="hljs-string">'12345-678'</span>
];

$addresses = [$homeAddress, $wordAddress];

<span class="hljs-comment">// Phones</span>
$workPhone = [
    <span class="hljs-string">'type'</span> =&gt; <span class="hljs-string">'work'</span>,
    <span class="hljs-string">'number'</span> =&gt; <span class="hljs-string">'001 555-1234'</span>,
    <span class="hljs-string">'cellPhone'</span> =&gt; <span class="hljs-literal">false</span>
];
$homePhone = [
    <span class="hljs-string">'type'</span> =&gt; <span class="hljs-string">'home'</span>,
    <span class="hljs-string">'number'</span> =&gt; <span class="hljs-string">'001 555-4321'</span>,
    <span class="hljs-string">'cellPhone'</span> =&gt; <span class="hljs-literal">false</span>
];
$cellPhone = [
    <span class="hljs-string">'type'</span> =&gt; <span class="hljs-string">'work'</span>,
    <span class="hljs-string">'number'</span> =&gt; <span class="hljs-string">'001 9999-8888'</span>,
    <span class="hljs-string">'cellPhone'</span> =&gt; <span class="hljs-literal">true</span>
];

$phones = [$workPhone, $homePhone, $cellPhone];

QRCode::vCard($firstName, $lastName, $title, $email, $company, $job, $url, $addresses, $phones)
    -&gt;setErrorCorrectionLevel(<span class="hljs-string">'H'</span>)
    -&gt;setSize(<span class="hljs-number">4</span>)
    -&gt;setMargin(<span class="hljs-number">2</span>)
    -&gt;svg();
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Since this QR code stores a lot of information, it's recommended to store it in a smaller pixel size and as SVG, so it can be re-scaled as needed. The error correction level should be set to high.</div>
</div>

<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks to the Laravel QR Code package it's really easy to create QR codes in Laravel. For more detailed information, check out the <a target="_blank" href="https://github.com/giauphan/laravel-qr-code">GitHub repository</a> and the <a target="_blank" href="https://github.com/giauphan/laravel-qr-code/blob/master/docs/index.md">official documentation</a>.</p>
<hr />
<p><strong>Thank you for reading!</strong> 🙏</p>
<p>Do you find this article helpful? <a target="_blank" href="https://linu.us/sponsor">Support my work on Hashnode</a> 💚</p>
<p>I'm also on <a target="_blank" href="https://x.com/linusbenkner">𝕏 / linusbenkner</a> - would love to connect!</p>
<hr />
<p>Happy coding 👨‍💻</p>
]]></content:encoded></item><item><title><![CDATA[Create a WordPress Plugin from Scratch - Full Beginners Guide]]></title><description><![CDATA[WordPress is the most used content management system on the planet. So today we’re taking a closer look at how to create custom plugins to extend the functionality of WordPress.
Why Create a Custom Plugin?
WordPress has many features already built-in...]]></description><link>https://linu.us/create-a-wordpress-plugin-from-scratch-full-beginners-guide</link><guid isPermaLink="true">https://linu.us/create-a-wordpress-plugin-from-scratch-full-beginners-guide</guid><category><![CDATA[WordPress]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[guide]]></category><category><![CDATA[beginnersguide]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Fri, 21 Oct 2022 06:35:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1666289582927/CjMOW1TEQ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>WordPress is the most used content management system on the planet. So today we’re taking a closer look at how to create custom plugins to extend the functionality of WordPress.</p>
<h2 id="heading-why-create-a-custom-plugin">Why Create a Custom Plugin?</h2>
<p>WordPress has many features already built-in to build and customize your website, and with <a target="_blank" href="https://wordpress.org/gutenberg/">the Gutenberg Project</a> WordPress is transforming the way you share content on the web.</p>
<p>However, if you want to customize or extend core functionality (and not only the design) you can use plugins to do just that. There’s a good change the <a target="_blank" href="https://wordpress.org/plugins/">WordPress plugin directory</a> already has a plugin for your use-case, but if not, you have to build your own.</p>
<h2 id="heading-setting-up-wordpress-and-a-local-development-environment"><strong>Setting up WordPress and a local development environment</strong></h2>
<p>Before we can create a plugin for WordPress, we need to have a working WordPress website. To follow along, you don’t need to have this website hosted somewhere - all you need is a webserver with PHP and a MySQL database running on your local computer.</p>
<h3 id="heading-windows"><strong>Windows</strong></h3>
<ol>
<li><p><a target="_blank" href="https://www.apachefriends.org/index.html">Download and install XAMPP</a> for Windows</p>
</li>
<li><p>Start XAMPP and start Apache and MySQL</p>
</li>
<li><p>Open <a target="_blank" href="http://localhost/">http://localhost</a> and verify that everything works</p>
</li>
</ol>
<p>The root-directory of your webserver is located here:</p>
<pre><code class="lang-bash">C:/xampp/htdocs
</code></pre>
<p>PhpMyAdmin is available at <code>http://localhost/phpMyAdmin</code> to manage your databases (username <code>root</code>, no password).</p>
<p>The URL of your local webserver is <code>http://localhost</code>.</p>
<h3 id="heading-macos"><strong>macOS</strong></h3>
<ol>
<li><p><a target="_blank" href="https://www.mamp.info/en/mamp/mac/">Download and install MAMP</a> for macOS</p>
</li>
<li><p>Start MAMP and click on “Preferences”</p>
</li>
<li><p>Under “Ports”, make sure the Apache port is 80</p>
</li>
<li><p>Open <a target="_blank" href="http://localhost/">http://localhost</a> and verify that everything works</p>
</li>
</ol>
<p>The root-directory of your webserver is located here:</p>
<pre><code class="lang-bash">/Applications/MAMP/htdocs
</code></pre>
<p>PhpMyAdmin is available at <code>http://localhost/phpMyAdmin</code> to manage your databases (username <code>root</code>, password <code>root</code>).</p>
<p>The URL of your local webserver is <code>http://localhost</code>.</p>
<h3 id="heading-setting-up-wordpress"><strong>Setting up WordPress</strong></h3>
<p>Create a new folder in your root-directory (see above) for your new WordPress installation. I’ll call it <code>custom-wp-plugin</code> which means my WordPress website will be available at <code>http://localhost/custom-wp-plugin</code>. You can also use a different name don’t use a separate folder at all.</p>
<p>Next, head over to <a target="_blank" href="https://wordpress.org/download/">wordpress.org to download the latest WordPress version</a>. Extract the downloaded zip-archive to the new folder (previous step).</p>
<p>We also need a database, so open up phpMyAdmin and create a new database. To keep it organized, I’ll call it just like the folder of my WordPress installation, <code>custom-wp-plugin</code>.</p>
<p>Now the installation and database are ready, open the website in your browser (for me that’s <code>http://localhost/custom-wp-plugin</code>).</p>
<p>Select your language, enter your database credentials and hit “Run the installation”.</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wordpress-install-db.png" alt="WordPress installation screen - database setup" /></p>
<p>After the database setup, you’ll be prompted to setup your new website. Since this website is just running on my computer and only used to test the plugin, I use admin as username and password. If you want to use this in production on a server, make sure to use secure credentials.</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wordpress-install-setup.png" alt="WordPress install screen - website setup" /></p>
<p>Now hit “Install WordPress” and then “Log In” and login with the account you just created (admin &amp; admin in my case).</p>
<p>WordPress is now installed and ready to use!</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wordpress-install-welcome.png" alt="The WordPress Dashboard after installation" /></p>
<h2 id="heading-creating-the-foundation"><strong>Creating the Foundation</strong></h2>
<p>WordPress stores customizations and content in different locations:</p>
<ul>
<li><p>Uploads (images, videos, …): <code>&lt;root-folder&gt;/wp-content/uploads</code></p>
</li>
<li><p>Themes: <code>&lt;root-folder&gt;/wp-content/themes</code></p>
</li>
<li><p>Plugins: <code>&lt;root-folder&gt;/wp-content/plugins</code></p>
</li>
<li><p>Data (pages, settings, articles, …): MySQL Database</p>
</li>
</ul>
<p>In the plugins directory, each plugin has its own folder. So, let’s create one:</p>
<pre><code class="lang-bash">&lt;root-folder&gt;/wp-content/plugins/example-plugin
</code></pre>
<p>This folder will contain all the files needed for the plugin. And if you want to share the plugin with others, you can just put it in a zip-archive and upload it to another WordPress website.</p>
<p>Now open this folder using your favorite IDE or editor (like <a target="_blank" href="https://www.jetbrains.com/phpstorm/">PhpStorm</a> or <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a>). By the way, if you’re using Vim or NeoVim, I have written a full article on how to set it up for PHP (and WordPress) development:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://linu.us/neovim-setup-for-php-and-nodejs-development">https://linu.us/neovim-setup-for-php-and-nodejs-development</a></div>
<p> </p>
<h3 id="heading-create-the-main-file"><strong>Create the main file</strong></h3>
<p>The main file of any WordPress plugin is the PHP file with the same name as the plugin folder, in this case <code>example-plugin</code>. That means we have to create a new file called <code>example-plugin.php</code> in our plugin’s directory.</p>
<p>To get our plugin shown in the WordPress plugin manager, we have to add a comment to the top of the file with some information about the plugin:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/**
 * Plugin Name: Example Plugin
 * Version: 1.1
 */</span>
</code></pre>
<p>You can also add more details if you want:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">/**
 * Plugin Name: Example Plugin
 * Version: 1.1
 * Author: Linus Benkner
 * Author URI: https://blog.linu.us
 * Plugin URI: https://example-plugin.com
 * Description: A custom WordPress Plugin
 */</span>
</code></pre>
<h3 id="heading-activate-the-plugin">Activate the Plugin</h3>
<p>Head over to your WordPress Dashboard and go to “Plugins”. You should see the plugin listed:</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-plugin-list.png" alt="WordPress Dashboard - Plugin list with the example plugin" /></p>
<p><em>(I have deleted the default plugins “Akismet Anti-Spam” and “Hello Dolly”. You probably see these plugins listed there as well.)</em></p>
<p>You can now activate the plugin by clicking “Activate”.</p>
<h2 id="heading-understanding-actions-and-filters"><strong>Understanding Actions and Filters</strong></h2>
<p>Before we customize WordPress, it’s important to understand how this even works. In WordPress, nearly everything can be customized using actions and filters.</p>
<h3 id="heading-actions">Actions</h3>
<p>An action is a function that can be triggered somewhere else. For example:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Listen to an action</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHello</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello world!"</span>;
}
add_action(<span class="hljs-string">"say_hello"</span>, <span class="hljs-string">"sayHello"</span>);

<span class="hljs-comment">// Trigger an action</span>
do_action(<span class="hljs-string">"say_hello"</span>);
</code></pre>
<p>The <code>do_action</code> function can also pass data to all the listening functions:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Listen to an action</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHello</span>(<span class="hljs-params">$name</span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello "</span> . $name;
}
add_action(<span class="hljs-string">"say_hello"</span>, <span class="hljs-string">"sayHello"</span>);

<span class="hljs-comment">// Trigger an action</span>
do_action(<span class="hljs-string">"say_hello"</span>, <span class="hljs-string">"Linus"</span>);
</code></pre>
<p>It is also possible to remove actions:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Listen to an action</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHello</span>(<span class="hljs-params">$name</span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello "</span> . $name;
}
add_action(<span class="hljs-string">"say_hello"</span>, <span class="hljs-string">"sayHello"</span>);

<span class="hljs-comment">// Remove it</span>
remove_action(<span class="hljs-string">"say_hello"</span>, <span class="hljs-string">"sayHello"</span>);

<span class="hljs-comment">// Trigger an action</span>
do_action(<span class="hljs-string">"say_hello"</span>, <span class="hljs-string">"Linus"</span>);
</code></pre>
<p>Now, obviously it doesn’t make much sense to create an action and then immediately remove it. Most often, <code>remove_action</code> is used to remove actions someone else created, for example WordPress itself.</p>
<h3 id="heading-filters"><strong>Filters</strong></h3>
<p>Filters are pretty similar to actions, but with the key difference that they return a value:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Create a filter</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">applyTaxes</span>(<span class="hljs-params">$price</span>) </span>{
    <span class="hljs-keyword">return</span> $price * <span class="hljs-number">1.19</span>;
}
add_filter(<span class="hljs-string">"final_price"</span>, <span class="hljs-string">"applyTaxes"</span>);

<span class="hljs-comment">// Filter something</span>
$price = apply_filters(<span class="hljs-string">"final_price"</span>, <span class="hljs-number">100</span>);
<span class="hljs-keyword">echo</span> $price; <span class="hljs-comment">// 119</span>
</code></pre>
<p>When you have multiple filters, you can order them using a number as third argument. For example, we want to first reduce the price by 30 and then add taxes:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Create a filter</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">applyTaxes</span>(<span class="hljs-params">$price</span>) </span>{
    <span class="hljs-keyword">return</span> $price * <span class="hljs-number">1.19</span>;
}
add_filter(<span class="hljs-string">"final_price"</span>, <span class="hljs-string">"applyTaxes"</span>, <span class="hljs-number">2</span>); <span class="hljs-comment">// &lt;- Priority: 2</span>

<span class="hljs-comment">// Create a second filter</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">applySpecialOfferSale</span>(<span class="hljs-params">$price</span>) </span>{
    <span class="hljs-keyword">return</span> $price - <span class="hljs-number">30</span>;
}
add_filter(<span class="hljs-string">"final_price"</span>, <span class="hljs-string">"applySpecialOfferSale"</span>, <span class="hljs-number">1</span>); <span class="hljs-comment">// &lt;- Priority: 1</span>

<span class="hljs-comment">// Filter something</span>
$price = apply_filters(<span class="hljs-string">"final_price"</span>, <span class="hljs-number">100</span>);
<span class="hljs-keyword">echo</span> $price; <span class="hljs-comment">// 83.3</span>
</code></pre>
<p>Actions can be prioritized the same way.</p>
<h2 id="heading-adding-a-widget-to-the-wordpress-dashboard"><strong>Adding a Widget to the WordPress Dashboard</strong></h2>
<p>In our main plugin file, let’s listen to the following action: <a target="_blank" href="https://developer.wordpress.org/reference/hooks/wp_dashboard_setup/">wp_dashboard_setup</a>. That’s an action by WordPress, executed when the Dashboard is requested:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">create_custom_dashboard_widget</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Todo</span>
}
add_action(<span class="hljs-string">"wp_dashboard_setup"</span>, <span class="hljs-string">"create_custom_dashboard_widget"</span>);
</code></pre>
<p>Inside this function, we can register a new Widget to the Dashboard using the <a target="_blank" href="https://developer.wordpress.org/reference/functions/wp_add_dashboard_widget/">wp_add_dashboard_widget</a> function:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">create_custom_dashboard_widget</span>(<span class="hljs-params"></span>) </span>{
    wp_add_dashboard_widget(
        <span class="hljs-string">"example_plugin_widget"</span>, <span class="hljs-comment">// Widget ID</span>
        <span class="hljs-string">"Example Plugin Widget"</span>, <span class="hljs-comment">// Widget Name</span>
        <span class="hljs-string">"render_example_plugin_widget"</span> <span class="hljs-comment">// Callback function</span>
    );
}
add_action(<span class="hljs-string">"wp_dashboard_setup"</span>, <span class="hljs-string">"create_custom_dashboard_widget"</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render_example_plugin_widget</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello World!"</span>;
}
</code></pre>
<p>In your WordPress Dashboard, you should see the new Widget:</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-plugin-custom-widget.png" alt="WordPress Dashboard with the new Example Plugin Widget" /></p>
<h2 id="heading-add-custom-content-to-pages"><strong>Add Custom Content to Pages</strong></h2>
<p>Let’s shift gears to the actual content of a website. For this example, we’ll add a floating link to each page.</p>
<h3 id="heading-append-html-to-the-content"><strong>Append HTML to the Content</strong></h3>
<p>To build a floating link, we need to add some HTML to the page. And you might have guessed it, there is an action to do just that. So, we can use the <a target="_blank" href="https://developer.wordpress.org/reference/hooks/wp_footer/">wp_footer</a> action to echo some HTML that will be printed at the bottom of the page:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">append_floating_link</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;a href='https://blog.linu.us' id='floating-link'&gt;Click me!&lt;/a&gt;"</span>;
}
add_action(<span class="hljs-string">"wp_footer"</span>, <span class="hljs-string">"append_floating_link"</span>);
</code></pre>
<p>On the home page, there is now a link at the bottom of the page.</p>
<h3 id="heading-register-stylesheets"><strong>Register Stylesheets</strong></h3>
<p>Our link is not “floating” yet, we need some CSS to make that happen. Create a new CSS file called <code>style.css</code> and add some styling:</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#floating-link</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#225482</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.8rem</span> <span class="hljs-number">1.4rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.4rem</span>;
}
</code></pre>
<p>Now we can enqueue the stylesheet inside the <code>wp_enqueue_scripts</code> action:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">register_plugin_styles</span>(<span class="hljs-params"></span>) </span>{
    $plugin_url = plugin_dir_url(<span class="hljs-keyword">__FILE__</span>); <span class="hljs-comment">// &lt;website_url&gt;/wp-content/plugins/&lt;plugin_name&gt;/</span>
    wp_enqueue_style(<span class="hljs-string">"custom_plugin_style"</span>, $plugin_url . <span class="hljs-string">"style.css"</span>);
}
add_action(<span class="hljs-string">"wp_enqueue_scripts"</span>, <span class="hljs-string">"register_plugin_styles"</span>);
</code></pre>
<p>On the page, you should now see a blue button:</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-plugin-floating-link.png" alt="enter image description here" /></p>
<h3 id="heading-register-javascript"><strong>Register JavaScript</strong></h3>
<p>You might also need custom JavaScript on the page, so let’s add some as well. For example, we can ask the user if they really want to leave the page when they click on the button. We can do this by selecting the link by the ID, add a event listener and cancel the “click” event if window.prompt returns false (the user denied). Create a new script <code>script.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> floatingLink = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"floating-link"</span>);

floatingLink.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.confirm(<span class="hljs-string">"Are you sure?"</span>)) {
    <span class="hljs-comment">// Do nothing</span>
  } <span class="hljs-keyword">else</span> {
    event.preventDefault();
  }
});
</code></pre>
<p>We can add a script like we add a stylesheet:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">register_plugin_styles</span>(<span class="hljs-params"></span>) </span>{
    $plugin_url = plugin_dir_url(<span class="hljs-keyword">__FILE__</span>); <span class="hljs-comment">// &lt;website_url&gt;/wp-content/plugins/&lt;plugin_name&gt;/</span>
    wp_enqueue_style(<span class="hljs-string">"custom_plugin_style"</span>, $plugin_url . <span class="hljs-string">"style.css"</span>);

    <span class="hljs-comment">// Register the script:</span>
    wp_enqueue_script(<span class="hljs-string">"custom_plugin_script"</span>, $plugin_url . <span class="hljs-string">"script.js"</span>, [], <span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>);
}
add_action(<span class="hljs-string">"wp_enqueue_scripts"</span>, <span class="hljs-string">"register_plugin_styles"</span>);
</code></pre>
<p><strong>Note:</strong> I’ve added 3 more arguments <code>[], false, true</code>:</p>
<ul>
<li><p><code>[]</code>: Dependencies - not needed, that’s the default value</p>
</li>
<li><p><code>false</code>: Version - not needed, that’s the default value</p>
</li>
<li><p><code>true</code>: Load script in footer - this has to be true because if the script is loaded in the head of the document, we can’t directly access the link (because the script is loaded before the content is ready)</p>
</li>
</ul>
<p>If you refresh the page and then click on the link, your browser will ask you if you really want to quit. With that, we now have added custom HTML, CSS and JavaScript to the page!</p>
<h2 id="heading-creating-a-settings-page"><strong>Creating a Settings Page</strong></h2>
<p>Settings pages are a great way to give certain WordPress users (administrators, editors, …) an option to customize the behavior of your plugin and / or display data.</p>
<p>Let’s build a settings page to customize the URL and text of our floating link.</p>
<h3 id="heading-how-options-work"><strong>How Options Work</strong></h3>
<p>WordPress has an extra database table just for settings like URL, theme, site name, and more. These options can be accessed using the <a target="_blank" href="https://developer.wordpress.org/reference/functions/get_option/">get_option</a> function:</p>
<pre><code class="lang-php">$websiteName = get_option(<span class="hljs-string">"blogname"</span>); <span class="hljs-comment">// Custom WP Plugin</span>
$websiteURL = get_option(<span class="hljs-string">"siteurl"</span>); <span class="hljs-comment">// http://localhost/custom-wp-plugin</span>
</code></pre>
<p>The cool thing about this is, we’re able to add our own options to the table with the <a target="_blank" href="https://developer.wordpress.org/reference/functions/add_option/">add_option</a> function:</p>
<pre><code class="lang-php">add_option(
    <span class="hljs-string">"example_plugin_button_url"</span>, <span class="hljs-comment">// name of the option</span>
    <span class="hljs-string">"https://google.com"</span> <span class="hljs-comment">// default value</span>
);
</code></pre>
<p>Now, whenever we need to get the current value of the option, we can just use <code>get_option</code>:</p>
<pre><code class="lang-php">$buttonURL = get_option(<span class="hljs-string">"example_plugin_button_url"</span>);
<span class="hljs-keyword">echo</span> $buttonURL; <span class="hljs-comment">// https://google.com</span>
</code></pre>
<p>And if we want to update the value, there is a function called <a target="_blank" href="https://developer.wordpress.org/reference/functions/update_option/">update_option</a>:</p>
<pre><code class="lang-php">update_option(<span class="hljs-string">"example_plugin_button_url"</span>, <span class="hljs-string">"https://blog.linu.us"</span>);

$buttonURL = get_option(<span class="hljs-string">"example_plugin_button_url"</span>);
<span class="hljs-keyword">echo</span> $buttonURL; <span class="hljs-comment">// https://blog.linu.us</span>
</code></pre>
<h3 id="heading-how-the-settings-page-is-structured">How the Settings Page is Structured</h3>
<p>A settings page is a separate page in the WordPress Dashboard that’s only visible to users with a certain capability (e.g., only to administrators or editors).</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-plugin-settingspage-structure.png" alt="Structure of the WordPress Settings Page" /></p>
<h3 id="heading-creating-the-settings-page"><strong>Creating the Settings Page</strong></h3>
<p>First, we need the page itself. We start by registering a new menu page:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Add the page to the menu</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example_plugin_options_page</span>(<span class="hljs-params"></span>) </span>{
    add_menu_page(
        <span class="hljs-string">'Example Plugin'</span>, <span class="hljs-comment">// page title</span>
        <span class="hljs-string">'Example Plugin Options'</span>, <span class="hljs-comment">// menu title</span>
        <span class="hljs-string">'manage_options'</span>, <span class="hljs-comment">// required capability / permission</span>
        <span class="hljs-string">'example_plugin_page'</span>, <span class="hljs-comment">// slug</span>
        <span class="hljs-string">'example_plugin_options_html'</span> <span class="hljs-comment">// callback function</span>
    );
}
add_action(<span class="hljs-string">'admin_menu'</span>, <span class="hljs-string">'example_plugin_options_page'</span>);
</code></pre>
<p>Whenever the page is requested (by clicking the link in the menu), the callback function <code>example_plugin_options_html</code> will output the HTML for the settings page. So let’s create this function:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Callback function for the page</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example_plugin_options_html</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Stop here if user doesn't have required capabilities</span>
    <span class="hljs-keyword">if</span> (!current_user_can(<span class="hljs-string">'manage_options'</span>)) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Create a message after successful update</span>
    <span class="hljs-keyword">if</span>(<span class="hljs-keyword">isset</span>($_GET[<span class="hljs-string">'settings-updated'</span>])) {
        add_settings_error(
            <span class="hljs-string">'example_plugin_messages'</span>, <span class="hljs-string">'example_plugin_message'</span>,
            <span class="hljs-string">'Settings saved!'</span>, <span class="hljs-comment">// message</span>
            <span class="hljs-string">'updated'</span> <span class="hljs-comment">// type (error|success|warning|info)</span>
        );
    }

    <span class="hljs-comment">// Display the messages</span>
    settings_errors( <span class="hljs-string">'example_plugin_messages'</span> );

    <span class="hljs-comment">// Form HTML</span>
    <span class="hljs-meta">?&gt;</span>
    &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">wrap</span>"&gt;
        &lt;<span class="hljs-title">h1</span>&gt;&lt;?<span class="hljs-title">php</span> <span class="hljs-title">echo</span> <span class="hljs-title">esc_html</span>(<span class="hljs-title">get_admin_page_title</span>()); ?&gt;&lt;/<span class="hljs-title">h1</span>&gt;
        &lt;<span class="hljs-title">form</span> <span class="hljs-title">action</span>="<span class="hljs-title">options</span>.<span class="hljs-title">php</span>" <span class="hljs-title">method</span>="<span class="hljs-title">post</span>"&gt;
            &lt;?<span class="hljs-title">php</span>
            <span class="hljs-title">settings_fields</span>('<span class="hljs-title">example_plugin_page</span>'); // <span class="hljs-title">output</span> <span class="hljs-title">security</span> <span class="hljs-title">fields</span>
            <span class="hljs-title">do_settings_sections</span>('<span class="hljs-title">example_plugin_page</span>'); // <span class="hljs-title">output</span> <span class="hljs-title">the</span> <span class="hljs-title">sections</span>
            <span class="hljs-title">submit_button</span>('<span class="hljs-title">Save</span> <span class="hljs-title">Settings</span>'); // <span class="hljs-title">save</span> <span class="hljs-title">button</span>
            ?&gt;
        &lt;/<span class="hljs-title">form</span>&gt;
    &lt;/<span class="hljs-title">div</span>&gt;
    &lt;?<span class="hljs-title">php</span>
}</span>
</code></pre>
<p>Now, you can open your WordPress Dashboard:</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-plugin-options-page-structure.png" alt="Custom WordPress Settings page with title and save button" /></p>
<p>With this, the settings page itself is created.</p>
<h3 id="heading-creating-the-section"><strong>Creating the Section</strong></h3>
<p>Inside the <code>admin_init</code> action, we can use the <a target="_blank" href="https://developer.wordpress.org/reference/functions/add_settings_section/">add_settings_section</a> function to add a section to out settings page:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example_plugin_settings_init</span>(<span class="hljs-params"></span>) </span>{
    add_settings_section(
        <span class="hljs-string">'example_plugin_section_link'</span>, <span class="hljs-comment">// section ID</span>
        <span class="hljs-string">'Floating Link'</span>, <span class="hljs-comment">// section title</span>
        <span class="hljs-string">'example_plugin_section_link_callback'</span>, <span class="hljs-comment">// callback function</span>
        <span class="hljs-string">'example_plugin_page'</span> <span class="hljs-comment">// settings page ID</span>
    );

    <span class="hljs-comment">// <span class="hljs-doctag">ToDo:</span> Register the field</span>
}
add_action(<span class="hljs-string">'admin_init'</span>, <span class="hljs-string">'example_plugin_settings_init'</span>);
</code></pre>
<p>Again, the section needs a callback function that renders the section:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example_plugin_section_link_callback</span>(<span class="hljs-params">$args</span>) </span>{
    <span class="hljs-meta">?&gt;</span>
    &lt;p id=<span class="hljs-string">"&lt;?php echo esc_attr(<span class="hljs-subst">$args</span>['id']); ?&gt;"</span>&gt;
        Settings <span class="hljs-keyword">for</span> the floating link.
    &lt;/p&gt;
    <span class="hljs-meta">&lt;?php</span>
}
</code></pre>
<p>Now our settings page looks like this:</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-plugin-settingspage-section.png" alt="WordPress Settings Page with the section" /></p>
<p>Finally, let’s add the field. In the action where we registered the section, I’ve added a comment “ToDo: Register the field”. That’s where we can now register the field:</p>
<pre><code class="lang-php">register_setting(
    <span class="hljs-string">'example_plugin_page'</span>, <span class="hljs-comment">// settings page</span>
    <span class="hljs-string">'example_plugin_button_url'</span> <span class="hljs-comment">// option name</span>
);
add_settings_field(
    <span class="hljs-string">'example_plugin_field_url'</span>, <span class="hljs-comment">// field ID</span>
    <span class="hljs-string">'URL'</span>, <span class="hljs-comment">// field title</span>
    <span class="hljs-string">'example_plugin_field_url_cb'</span>, <span class="hljs-comment">// callback function</span>
    <span class="hljs-string">'example_plugin_page'</span>, <span class="hljs-comment">// settings page</span>
    <span class="hljs-string">'example_plugin_section_link'</span> <span class="hljs-comment">// section</span>
);
</code></pre>
<p>And you guessed it - we need a callback function for the field:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example_plugin_field_url_cb</span>(<span class="hljs-params">$args</span>) </span>{
    <span class="hljs-comment">// Get the current value for the option</span>
    $url = get_option(<span class="hljs-string">'example_plugin_button_url'</span>);

    <span class="hljs-comment">// Output the field</span>
    <span class="hljs-meta">?&gt;</span>
    &lt;input
        type=<span class="hljs-string">"text"</span>
        id=<span class="hljs-string">"example_plugin_button_url"</span>
        value=<span class="hljs-string">"&lt;?php echo esc_attr(<span class="hljs-subst">$url</span>); ?&gt;"</span>
        name=<span class="hljs-string">"example_plugin_button_url"</span>
    &gt;
    &lt;p <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">description</span>"&gt;
        <span class="hljs-title">The</span> <span class="hljs-title">URL</span> <span class="hljs-title">users</span> <span class="hljs-title">will</span> <span class="hljs-title">be</span> <span class="hljs-title">sent</span> <span class="hljs-title">to</span> <span class="hljs-title">after</span> <span class="hljs-title">clicking</span> <span class="hljs-title">the</span> <span class="hljs-title">floating</span> <span class="hljs-title">link</span>.
    &lt;/<span class="hljs-title">p</span>&gt;
    &lt;?<span class="hljs-title">php</span>
}</span>
</code></pre>
<p>And that’s it! You can now see the currently stored value and change it:</p>
<p><img src="https://eu2.contabostorage.com/94908d89b1e54a0ba6a0ca80cfefa927:linu.us-blog/wp-settingspage-field-success.png" alt="Custom WordPress Settings page in action" /></p>
<h3 id="heading-make-use-of-the-option"><strong>Make use of the option</strong></h3>
<p>One last thing: When we created the HTML for our floating link, we hardcoded the link:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">append_floating_link</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;a href='https://blog.linu.us' id='floating-link'&gt;Click me!&lt;/a&gt;"</span>;
}
add_action(<span class="hljs-string">"wp_footer"</span>, <span class="hljs-string">"append_floating_link"</span>);
</code></pre>
<p>We can now change this to use our new option, so it actually uses the URL entered in the settings page:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">append_floating_link</span>(<span class="hljs-params"></span>) </span>{
    $url = get_option(<span class="hljs-string">"example_plugin_button_url"</span>);
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"&lt;a href='"</span> . $url . <span class="hljs-string">"' id='floating-link'&gt;Click me!&lt;/a&gt;"</span>;
}
add_action(<span class="hljs-string">"wp_footer"</span>, <span class="hljs-string">"append_floating_link"</span>);
</code></pre>
<h2 id="heading-next-steps"><strong>Next Steps</strong></h2>
<p>Thank you for following along this guide. You now know:</p>
<ul>
<li><p>How to set WordPress up locally</p>
</li>
<li><p>How to create a custom plugin for WordPress</p>
</li>
<li><p>How to use actions and filters (with the example of adding custom HTML to the page)</p>
</li>
<li><p>How to add custom CSS and JavaScript to the page</p>
</li>
<li><p>How to create a custom settings page for your plugin</p>
</li>
</ul>
<p>Before you leave, here are some helpful resources:</p>
<ul>
<li><p><a target="_blank" href="https://developer.wordpress.org/"><strong>WordPress Developer Resources</strong></a>: Detailed documentation of all functions, actions and filters WordPress provides</p>
</li>
<li><p><a target="_blank" href="https://adambrown.info/p/wp_hooks/hook"><strong>List of all WordPress hooks (actions &amp; filters)</strong></a>: Great to quickly search for an action or filter, links directly to the documentation for each hook</p>
</li>
<li><p><a target="_blank" href="https://wordpress.stackexchange.com/"><strong>WordPress on StackExchange</strong></a>: The StackOverflow for WordPress, ask questions if you need help or browse through more than 100.000 questions</p>
</li>
</ul>
<p>Thank you so much for reading, happy coding and have a great day 🤗👋</p>
]]></content:encoded></item><item><title><![CDATA[NeoVim Setup for PHP and NodeJS Development]]></title><description><![CDATA[Hi everyone! 👋
It's been a while since my last post, and today I want to show you how I sped up my development in the last weeks.
Until recently, I've been a big fan of Visual Studio Code and it was my primary editor for the last years. Over time, I...]]></description><link>https://linu.us/neovim-setup-for-php-and-nodejs-development</link><guid isPermaLink="true">https://linu.us/neovim-setup-for-php-and-nodejs-development</guid><category><![CDATA[vim]]></category><category><![CDATA[neovim]]></category><category><![CDATA[Text Editors]]></category><category><![CDATA[guide]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Wed, 27 Jul 2022 22:02:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658959071613/pcct4OpB_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi everyone! 👋</p>
<p>It's been a while since my last post, and today I want to show you how I sped up my development in the last weeks.</p>
<p>Until recently, I've been a big fan of <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> and it was my primary editor for the last years. Over time, I learned more and more key bindings, started to really use the command palette and started to not do everything with the mouse - and guess what, it felt a lot faster than doing everything with the mouse.</p>
<p>Now, I still love VS Code, can really recommend it and I use it for example in the browser on GitHub. But since I tried <a target="_blank" href="https://www.vim.org/">Vim</a>, I'm now using this as my primary editor.</p>
<p><strong>Please note, I'm by far no (Neo)Vim expert and still learning it. In this article, I want to share my story about how I got into (Neo)Vim and what my setup looks like.</strong></p>
<h2 id="heading-why-vim">Why Vim?</h2>
<p>When I first heard about Vim, I was like "why in the world would you use Vim when there is VS Code and all the other modern IDE's?". The only thing I knew about Vim at this time was <a target="_blank" href="https://stackoverflow.com/questions/11828270/how-do-i-exit-vim">how to exit Vim</a>. But after a deeper look at Vim and some great input from a co-worker, I decided to at least try it for some time.</p>
<p>Here are the main reasons:</p>
<ul>
<li>🚀 Speed. I was really impressed by how fast you can be with Vim - even when you're still learning it.</li>
<li>🌐 I can use it on the server. That's awesome, because I always used a FTP client when I needed to edit files on the server. Now, I can work with files on the server just as I do with files on my computer - directly from the terminal.</li>
<li>⌨️ I didn't really know any terminal-based text editor before.</li>
</ul>
<h2 id="heading-how-i-learned-vim">How I learned Vim</h2>
<p>Quick answer: Learning by doing :)</p>
<p>I started to use only Vim for a few days, which forced me to learn Vim commands and key bindings. No VS Code or any other text editor - only Vim. At the beginning, I was really slow and constantly looking up how to do basic stuff.</p>
<p>But every day I got faster, learned new commands and improved my workflow. And until now, I didn't go back to VS Code.</p>
<p>That's my config for Vim:</p>
<pre><code class="lang-vim">set encoding=UTF-8
" Set encoding

set number
" Show line numbers by default

syntax on
" Enable syntax highlighting by default

filetype plugin indent on
" show existing tab with 4 spaces width
set tabstop=4
" when indenting with '&gt;', use 4 spaces width
set shiftwidth=4
" On pressing tab, insert 4 spaces
set expandtab

" Tab / Shift Tab to switch between tabs (:tabe &lt;file&gt;)
nnoremap &lt;Tab&gt; :tabnext&lt;CR&gt;
nnoremap &lt;S-Tab&gt; :tabprevious&lt;CR&gt;
</code></pre>
<h2 id="heading-neovim">NeoVim</h2>
<p>After using Vim for a few weeks, I now moved to NeoVim. I really like Vim now, but there are some VS Code features I'd love to have in Vim. That's why I decided to use NeoVim.</p>
<p>That's how I wanted my NeoVim setup to look like:</p>
<ul>
<li>Not too different to my normal Vim setup so I can still work with the default Vim editor if I can't use NeoVim (e.g. on servers)</li>
<li>Main use case:<ul>
<li>PHP (+ WordPress)</li>
<li>React / NextJS</li>
<li>Svelte / SvelteKit</li>
</ul>
</li>
</ul>
<p>You see, a pretty simple setup. So now let's actually set it up:</p>
<h2 id="heading-installation-andamp-setup">Installation &amp; Setup</h2>
<h3 id="heading-1-install-neovim">1. Install NeoVim</h3>
<p>Follow the instructions on the <a target="_blank" href="https://github.com/neovim/neovim/wiki/Installing-Neovim">NeoVim installation page</a> or use the following command:</p>
<p><strong>macOS (with brew):</strong> <code>brew install neovim</code></p>
<p><strong>Windows (with winget):</strong> <code>winget install NeoVim.NeoVim</code></p>
<h3 id="heading-2-install-vimplug">2. Install VimPlug</h3>
<p>To manage the plugins / extensions, I decided to use VimPlug as the plugin manager. Read the <a target="_blank" href="https://github.com/junegunn/vim-plug#neovim">installation guide here</a> or use the following command:</p>
<p><strong>macOS / Linux:</strong></p>
<pre><code class="lang-bash">sh -c <span class="hljs-string">'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
       https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'</span>
</code></pre>
<p><strong>Windows (PowerShell):</strong></p>
<pre><code class="lang-powershell">iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |`
    ni "$(@($env:XDG_DATA_HOME, $env:LOCALAPPDATA)[$null -eq $env:XDG_DATA_HOME])/nvim-data/site/autoload/plug.vim" -Force
</code></pre>
<h3 id="heading-3-neovim-config">3. NeoVim Config</h3>
<p>Create the NeoVim config file <code>~/.config/nvim/init.vim</code> and paste the following:</p>
<pre><code class="lang-vim">set encoding=UTF-8
set number
syntax on

" set tab to 4 spaces
filetype plugin indent on
set tabstop=4
set shiftwidth=4
set expandtab

" Tab / Shift Tab to navigate between tabs
nnoremap &lt;Tab&gt; :tabnext&lt;CR&gt;
nnoremap &lt;S-Tab&gt; :tabprevious&lt;CR&gt;

" CoC settings
set nobackup
set nowritebackup
set cmdheight=2
set updatetime=300
set shortmess+=c
if has("nvim-0.5.0") || has("patch-8.1.1564")
  set signcolumn=number
else
  set signcolumn=yes
endif
inoremap &lt;silent&gt;&lt;expr&gt; &lt;TAB&gt;
      \ pumvisible() ? "\&lt;C-n&gt;" :
      \ CheckBackspace() ? "\&lt;TAB&gt;" :
      \ coc#refresh()
inoremap &lt;expr&gt;&lt;S-TAB&gt; pumvisible() ? "\&lt;C-p&gt;" : "\&lt;C-h&gt;"
function! CheckBackspace() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction
if has('nvim')
  inoremap &lt;silent&gt;&lt;expr&gt; &lt;c-space&gt; coc#refresh()
else
  inoremap &lt;silent&gt;&lt;expr&gt; &lt;c-@&gt; coc#refresh()
endif
inoremap &lt;silent&gt;&lt;expr&gt; &lt;cr&gt; pumvisible() ? coc#_select_confirm()
                              \: "\&lt;C-g&gt;u\&lt;CR&gt;\&lt;c-r&gt;=coc#on_enter()\&lt;CR&gt;"
nmap &lt;silent&gt; [g &lt;Plug&gt;(coc-diagnostic-prev)
nmap &lt;silent&gt; ]g &lt;Plug&gt;(coc-diagnostic-next)
nmap &lt;silent&gt; gd &lt;Plug&gt;(coc-definition)
nmap &lt;silent&gt; gy &lt;Plug&gt;(coc-type-definition)
nmap &lt;silent&gt; gi &lt;Plug&gt;(coc-implementation)
nmap &lt;silent&gt; gr &lt;Plug&gt;(coc-references)
nnoremap &lt;silent&gt; K :call ShowDocumentation()&lt;CR&gt;
function! ShowDocumentation()
  if CocAction('hasProvider', 'hover')
    call CocActionAsync('doHover')
  else
    call feedkeys('K', 'in')
  endif
endfunction
autocmd CursorHold * silent call CocActionAsync('highlight')

" VimPlug plugin manager
call plug#begin()
Plug 'https://github.com/vim-airline/vim-airline'
Plug 'https://github.com/tpope/vim-commentary'
Plug 'https://github.com/ap/vim-css-color'
Plug 'https://github.com/rafi/awesome-vim-colorschemes'
Plug 'https://github.com/neoclide/coc.nvim'
Plug 'othree/html5.vim'
Plug 'pangloss/vim-javascript'
Plug 'evanleck/vim-svelte', {'branch': 'main'}
call plug#end()

" set the colorsheme
" list: https://github.com/rafi/awesome-vim-colorschemes
colorscheme minimalist
</code></pre>
<p>At the top, you can see some settings like the tab with, encoding and I also want syntax highlighting and line numbers enabled by default.</p>
<p>Next is a big config block for the <a target="_blank" href="https://github.com/neoclide/coc.nvim">CoC extension</a>. I can't really explain what all this is doing, I got this from the CoC installation &amp; setup guide. Basically, it enables the autocompletion window and creates some hotkeys like <kbd>K</kbd> to show the documentation.</p>
<p>Below that, you can find the VimPlug section. Here are all the plugins registered:</p>
<ul>
<li><a target="_blank" href="https://github.com/vim-airline/vim-airline">vim-airline</a>: status/tabline showing things like the current git branch and vim mode.</li>
<li><a target="_blank" href="https://github.com/tpope/vim-commentary">vim-commentary</a>: Comment stuff out, <code>gcc</code> to comment out lines</li>
<li><a target="_blank" href="https://github.com/ap/vim-css-color">vim-css-color</a>: Highlight color names</li>
<li><a target="_blank" href="https://github.com/rafi/awesome-vim-colorschemes">awesome-vim-colorschemes</a>: A big collection of color schemes, I'm using <code>minimalist</code> (set with <code>colorscheme &lt;name&gt;</code>)</li>
<li><a target="_blank" href="https://github.com/neoclide/coc.nvim">coc.nvim</a>: Language Server Support, completion, ...</li>
<li><a target="_blank" href="https://github.com/othree/html5.vim">html5.vim</a>: Syntax &amp; indentation for HTML5 and SVG (required for vim-svelte)</li>
<li><a target="_blank" href="https://github.com/pangloss/vim-javascript">vim-javascript</a>: Syntax &amp; indentation for JavaScript (required for vim-svelte)</li>
<li><a target="_blank" href="https://github.com/evanleck/vim-svelte">vim-svelte</a>: Syntax &amp; indentation for Svelte</li>
</ul>
<h3 id="heading-4-install-plugins">4. Install Plugins</h3>
<p>Because we changed the NeoVim config, restart NeoVim first. There may be some errors because the plugins aren't installed yet. Use VimPlug to install the plugins specified in the config file:</p>
<pre><code class="lang-vim">:PlugInstall
</code></pre>
<h3 id="heading-5-set-up-coc-extension">5. Set Up CoC Extension</h3>
<p>Once the plugins are installed, close NeoVim and navigate to the folder of the CoC extension:</p>
<p><strong>macOS / Linux:</strong> <code>~/.local/share/nvim/plugged/coc.nvim/</code></p>
<p><strong>Windows:</strong> <code>~/vimfiles/plugged/coc.nvim/</code></p>
<p>Now, install the dependencies and build the source code:</p>
<pre><code class="lang-bash">npm ci &amp;&amp; npm run build
</code></pre>
<h3 id="heading-6-extensions-for-the-coc-extension">6. Extensions for the CoC Extension</h3>
<p>You can now start NeoVim again and there shouldn't be any errors. To complete the setup, let's install some extensions for CoC, like a JS/TS language server and intelephense for PHP:</p>
<pre><code>:CocInstall coc<span class="hljs-operator">-</span>tsserver coc<span class="hljs-operator">-</span>json coc<span class="hljs-operator">-</span>svelte @yaegassy<span class="hljs-operator">/</span>coc<span class="hljs-operator">-</span>intelephense
</code></pre><h3 id="heading-7-optional-wordpress">7. Optional: WordPress</h3>
<p>I'm working with WordPress themes and plugins, but with the current setup every WordPress function is marked as error because the intelephense extension doesn't know I'm inside a WordPress theme with all the WordPress functions available. We can change that by opening the CoC config (<code>:CocConfig</code>) and adding "wordpress" to the list of <code>intelephense.stubs</code>. If you type "intelephense.stubs", you can use the autocompletion by CoC to create the whole list and then only append "wordpress" to the end of the list.</p>
<p>Full list:</p>
<pre><code>{
    <span class="hljs-attr">"intelephense.stubs"</span>: [
        <span class="hljs-string">"apache"</span>,
        <span class="hljs-string">"bcmath"</span>,
        <span class="hljs-string">"bz2"</span>,
        <span class="hljs-string">"calendar"</span>,
        <span class="hljs-string">"com_dotnet"</span>,
        <span class="hljs-string">"Core"</span>,
        <span class="hljs-string">"ctype"</span>,
        <span class="hljs-string">"curl"</span>,
        <span class="hljs-string">"date"</span>,
        <span class="hljs-string">"dba"</span>,
        <span class="hljs-string">"dom"</span>,
        <span class="hljs-string">"enchant"</span>,
        <span class="hljs-string">"exif"</span>,
        <span class="hljs-string">"FFI"</span>,
        <span class="hljs-string">"fileinfo"</span>,
        <span class="hljs-string">"filter"</span>,
        <span class="hljs-string">"fpm"</span>,
        <span class="hljs-string">"ftp"</span>,
        <span class="hljs-string">"gd"</span>,
        <span class="hljs-string">"gettext"</span>,
        <span class="hljs-string">"gmp"</span>,
        <span class="hljs-string">"hash"</span>,
        <span class="hljs-string">"iconv"</span>,
        <span class="hljs-string">"imap"</span>,
        <span class="hljs-string">"intl"</span>,
        <span class="hljs-string">"json"</span>,
        <span class="hljs-string">"ldap"</span>,
        <span class="hljs-string">"libxml"</span>,
        <span class="hljs-string">"mbstring"</span>,
        <span class="hljs-string">"meta"</span>,
        <span class="hljs-string">"mysqli"</span>,
        <span class="hljs-string">"oci8"</span>,
        <span class="hljs-string">"odbc"</span>,
        <span class="hljs-string">"openssl"</span>,
        <span class="hljs-string">"pcntl"</span>,
        <span class="hljs-string">"pcre"</span>,
        <span class="hljs-string">"PDO"</span>,
        <span class="hljs-string">"pdo_ibm"</span>,
        <span class="hljs-string">"pdo_mysql"</span>,
        <span class="hljs-string">"pdo_pgsql"</span>,
        <span class="hljs-string">"pdo_sqlite"</span>,
        <span class="hljs-string">"pgsql"</span>,
        <span class="hljs-string">"Phar"</span>,
        <span class="hljs-string">"posix"</span>,
        <span class="hljs-string">"pspell"</span>,
        <span class="hljs-string">"readline"</span>,
        <span class="hljs-string">"Reflection"</span>,
        <span class="hljs-string">"session"</span>,
        <span class="hljs-string">"shmop"</span>,
        <span class="hljs-string">"SimpleXML"</span>,
        <span class="hljs-string">"snmp"</span>,
        <span class="hljs-string">"soap"</span>,
        <span class="hljs-string">"sockets"</span>,
        <span class="hljs-string">"sodium"</span>,
        <span class="hljs-string">"SPL"</span>,
        <span class="hljs-string">"sqlite3"</span>,
        <span class="hljs-string">"standard"</span>,
        <span class="hljs-string">"superglobals"</span>,
        <span class="hljs-string">"sysvmsg"</span>,
        <span class="hljs-string">"sysvsem"</span>,
        <span class="hljs-string">"sysvshm"</span>,
        <span class="hljs-string">"tidy"</span>,
        <span class="hljs-string">"tokenizer"</span>,
        <span class="hljs-string">"xml"</span>,
        <span class="hljs-string">"xmlreader"</span>,
        <span class="hljs-string">"xmlrpc"</span>,
        <span class="hljs-string">"xmlwriter"</span>,
        <span class="hljs-string">"xsl"</span>,
        <span class="hljs-string">"Zend OPcache"</span>,
        <span class="hljs-string">"zip"</span>,
        <span class="hljs-string">"zlib"</span>,
        <span class="hljs-string">"wordpress"</span>
    ]
}
</code></pre><p>And that's my current NeoVim setup. Maybe this will help some of you who want to get started with NeoVim.</p>
<p>What's your favorite editor or IDE? Are you using Vim / NeoVim too? Let me know in the comments!</p>
<p>Thank you for reading, happy coding and have a great day! 😊🚀 </p>
]]></content:encoded></item><item><title><![CDATA[Use Contabo Object Storage with NodeJS]]></title><description><![CDATA[Hi there! 👋
Recently, Contabo launched their new storage solution Object Storage. These object storages provide an S3 compatible API and can be used quite easily - in this post, I show you how.
You can find the full source code for both NodeJS and P...]]></description><link>https://linu.us/contabo-object-storage-with-nodejs</link><guid isPermaLink="true">https://linu.us/contabo-object-storage-with-nodejs</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Express]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Tue, 29 Mar 2022 21:09:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885767805/5ilP66SMa.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi there! 👋</p>
<p>Recently, <a target="_blank" href="https://contabo.com/en/">Contabo</a> launched their new storage solution <a target="_blank" href="https://contabo.com/en/object-storage/">Object Storage</a>. These object storages provide an S3 compatible API and can be used quite easily - in this post, I show you how.</p>
<p><em>You can find the full source code for both NodeJS and Python3/Flask <a class="post-section-overview" href="#heading-full-code">at the bottom</a>.</em></p>
<h2 id="heading-get-your-object-storage">Get your Object Storage</h2>
<p>To follow along, you first need your own Object Storage. You can <a target="_blank" href="https://contabo.com/en/object-storage/order/">go directly to Contabo and order your Object Storage now</a>. After you've completed the purchase, you can log in to your <a target="_blank" href="https://my.contabo.com/object_storage">customer panel and select "Object Storage" in the sidebar</a>. If you've never done this before, you'll be asked to set a password for the Object Storage Panel. After you've set your password, you can log in to the <a target="_blank" href="https://new.contabo.com/">Object Storage Panel</a> with your Contabo Email and the newly set password.</p>
<p>Now navigate to "Storage" - "Object Storage (Buckets)" in the sidebar and you should see your new Object Storage. Perfect!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648582794389/AC5cuCksw.png" alt="Contabo Object Storage Panel - Storage Overview" />
<em>I didn't find an option to change the language in this panel, so everything is in german, I'm sorry :(</em></p>
<h2 id="heading-create-a-bucket">Create a bucket</h2>
<p>Your files are organized in buckets, so let's create our first bucket. Below your Object Storage in the Object Storage Panel is a button to create a new bucket.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648583176221/KCj-JIELN.png" alt="Create a new bucket for your storage in the Object Storage Panel" /></p>
<p>Now name your bucket and choose a region where your bucket should be located. Create it and you should see your new bucket on the list.</p>
<h2 id="heading-get-the-access-keys">Get the access keys</h2>
<p>To connect with the S3 API of your storage, we need two keys to authenticate:</p>
<ol>
<li>The <code>Access Key</code> and</li>
<li>the <code>Secret Key</code>.</li>
</ol>
<p>You can generate them in the Object Storage Panel by navigating to <a target="_blank" href="https://new.contabo.com/account/security">"Account" - "Security and access"</a>. Scroll down to the section "S3 Object Storage". That's where you can see both tokens. If you don't, click "Regenerate secret key" to create a new secret key.</p>
<p>We'll need both keys later when connecting to the S3 API.</p>
<h2 id="heading-setting-up-nodejs-and-express">Setting up NodeJS and Express</h2>
<p>For this example, I'll build a simple webserver with <a target="_blank" href="https://expressjs.com/">ExpressJS</a> to handle file uploads and also return all currently stored files.</p>
<p>Open up a new folder and initialize a new npm project. We also need <code>express</code> for our webserver and <code>aws-sdk</code> to interact with the S3 API, so we install them too:</p>
<pre><code class="lang-bash">npm init -y
npm i express aws-sdk
</code></pre>
<p>To get started we first need the webserver, so let's start express:</p>
<pre><code class="lang-js"><span class="hljs-comment">// index.js</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>)
<span class="hljs-keyword">const</span> AWS = <span class="hljs-built_in">require</span>(<span class="hljs-string">"aws-sdk"</span>)

<span class="hljs-keyword">const</span> app = express()

<span class="hljs-comment">/* 
 * Code goes here
 */</span>

app.listen(<span class="hljs-number">4000</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"🚀 App is running on http://localhost:4000"</span>)
})
</code></pre>
<h2 id="heading-create-the-routes">Create the routes</h2>
<p>For this simple project we need 3 routes:</p>
<ol>
<li><code>GET /</code>: The form to upload a file to our express server</li>
<li><code>POST /upload</code>: The form handler to upload the file to the storage bucket</li>
<li><code>GET /list</code>: A list of all files inside the storage bucket</li>
</ol>
<h3 id="heading-show-the-form">Show the form</h3>
<p>Well, before we can show a form, we first need a form. For this demo, I just created a quick HTML page <code>form.html</code> with a simple form on it to upload a file. Notice the form action <code>/upload</code>, this points to the route handling the file upload, and also the method <code>post</code>.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>File Upload<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Upload a file to Contabo Object Storage<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/upload"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span> <span class="hljs-attr">enctype</span>=<span class="hljs-string">"multipart/form-data"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">accept</span>=<span class="hljs-string">"image/png,image/jpeg"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"fileToUpload"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Upload the file<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>All we have left to do is use express to send the file on each GET request to the root path:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Show the form</span>
app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    response.sendFile(__dirname + <span class="hljs-string">"/form.html"</span>) <span class="hljs-comment">// &lt;- Point this to your HTML file containing the form</span>
})
</code></pre>
<p>Done! You can test it by running <code>node index.js</code> to start the express server and open <code>http://localhost:4000</code> in your browser. You should see the form.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648586726437/tFHtkU9ZJ.png" alt="Simple HTML form to upload a file" /></p>
<h3 id="heading-handle-the-file-upload">Handle the file upload</h3>
<p>To handle the file upload, we need the <code>express-fileupload</code> package to parse incoming <code>multipart/form-data</code> requests and extract the files:</p>
<pre><code class="lang-bash">npm i express-fileupload
</code></pre>
<p>And of course import it at the top of our <code>index.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>)
<span class="hljs-keyword">const</span> AWS = <span class="hljs-built_in">require</span>(<span class="hljs-string">"aws-sdk"</span>)

<span class="hljs-keyword">const</span> fileUpload = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express-fileupload'</span>) <span class="hljs-comment">// NEW</span>
</code></pre>
<p>Express-Fileupload is a middleware, so we need to register it to our express app:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> app = express()

<span class="hljs-comment">// Middleware to handle incoming files</span>
app.use(fileUpload({
    <span class="hljs-attr">createParentPath</span>: <span class="hljs-literal">true</span>
}))
</code></pre>
<p>In the next step, we create the actual route to handle the POST request with the file to upload:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Handle files</span>
app.post(<span class="hljs-string">"/upload"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    <span class="hljs-keyword">const</span> file = request?.files?.[<span class="hljs-string">"fileToUpload"</span>] || <span class="hljs-literal">null</span>

    <span class="hljs-comment">// Return if the request doesn't contain the file</span>
    <span class="hljs-keyword">if</span>(!file) <span class="hljs-keyword">return</span> response.sendStatus(<span class="hljs-number">400</span>)

    <span class="hljs-comment">/*
     * TODO Upload the file to the storage bucket
     */</span>

    response.sendStatus(<span class="hljs-number">200</span>)
})
</code></pre>
<h3 id="heading-connect-to-the-s3-api">Connect to the S3 API</h3>
<p>Things are getting real, we'll now connect to the S3 API to interact with our storage bucket!</p>
<p>Right after we create the express app by calling <code>express()</code>, let's create the connection to the S3 API:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> app = express()

<span class="hljs-comment">// Connect to S3</span>
<span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> AWS.S3({
    <span class="hljs-attr">endpoint</span>: <span class="hljs-string">`contabo_storage_bucket_url`</span>, <span class="hljs-comment">// e.g. https://eu2.contabostorage.com/bucketname</span>
    <span class="hljs-attr">accessKeyId</span>: <span class="hljs-string">"your_access_key_here"</span>,
    <span class="hljs-attr">secretAccessKey</span>: <span class="hljs-string">"your_secret_key_here"</span>,
    <span class="hljs-attr">s3BucketEndpoint</span>: <span class="hljs-literal">true</span>,
});
</code></pre>
<p>We can now use all the S3 functions with our new <code>s3</code> variable.</p>
<h3 id="heading-upload-the-file">Upload the file</h3>
<p>Back in our <code>POST /upload</code> route, we need to parse the data from the file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// inside POST /upload route</span>
<span class="hljs-keyword">const</span> file = request?.files?.[<span class="hljs-string">"fileToUpload"</span>] || <span class="hljs-literal">null</span>

<span class="hljs-comment">// Return if the request doesn't contain the file</span>
<span class="hljs-keyword">if</span>(!file) <span class="hljs-keyword">return</span> response.sendStatus(<span class="hljs-number">400</span>)

<span class="hljs-comment">// Destructure the content of the file object</span>
<span class="hljs-keyword">const</span> { name, mimetype, size, data } = file
<span class="hljs-keyword">const</span> fileContent  = Buffer.from(data, <span class="hljs-string">' '</span>);
</code></pre>
<p>We can now call the <code>s3.putObject</code> function to upload a file to the storage bucket.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Handle files</span>
app.post(<span class="hljs-string">"/upload"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    <span class="hljs-keyword">const</span> file = request?.files?.[<span class="hljs-string">"fileToUpload"</span>] || <span class="hljs-literal">null</span>

    <span class="hljs-comment">// Return if the request doesn't contain the file</span>
    <span class="hljs-keyword">if</span>(!file) <span class="hljs-keyword">return</span> response.sendStatus(<span class="hljs-number">400</span>)

    <span class="hljs-comment">// Destructure the content of the file object</span>
    <span class="hljs-keyword">const</span> { name, mimetype, size, data } = file
    <span class="hljs-keyword">const</span> fileContent  = Buffer.from(data, <span class="hljs-string">' '</span>);

    <span class="hljs-comment">/* Add security checks (e.g. max size) here */</span>

    s3.putObject({
        <span class="hljs-attr">Body</span>: fileContent, <span class="hljs-comment">// The actual file content</span>
        <span class="hljs-attr">Bucket</span>: <span class="hljs-string">"bucket_name"</span>,
        <span class="hljs-attr">Key</span>: name, <span class="hljs-comment">// The name of the file</span>
    }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, data</span>) </span>{
        <span class="hljs-keyword">if</span> (err) {
            response.sendStatus(<span class="hljs-number">500</span>)
        } <span class="hljs-keyword">else</span> {
            response.sendStatus(<span class="hljs-number">200</span>)
        }
    });
})
</code></pre>
<blockquote>
<p><strong>Note:</strong> This is for demo purposes only. When used in production, make sure you validate the file before uploading (name, file size, file type, ...). I've added a comment in the code below where you should validation and security checks.</p>
</blockquote>
<h3 id="heading-list-all-the-files">List all the files</h3>
<p>You may want to show all uploaded files (e.g. inside a media browser), so let's also implement the route to list all files inside the bucket. For this demo, I'll just return the full result from the S3 API as JSON, but you can do whatever you want with it.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Show all files</span>
app.get(<span class="hljs-string">"/list"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    <span class="hljs-comment">// Get all objects inside the bucket</span>
    s3.listObjects({
        <span class="hljs-attr">Bucket</span>: <span class="hljs-string">"bucket_name"</span>,
    }, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, data</span>) </span>{
        <span class="hljs-keyword">if</span> (err) {
            response.sendStatus(<span class="hljs-number">500</span>)
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Return the list ("Contents") as JSON</span>
            response.json(data.Contents)
        }
    })
})
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648586868796/SsnKI9l5m.png" alt="JSON list of all objects in the storage bucket" /></p>
<h2 id="heading-full-code">Full Code</h2>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>)
<span class="hljs-keyword">const</span> AWS = <span class="hljs-built_in">require</span>(<span class="hljs-string">"aws-sdk"</span>)

<span class="hljs-keyword">const</span> fileUpload = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express-fileupload'</span>)

<span class="hljs-keyword">const</span> app = express()

<span class="hljs-comment">// Connect to S3</span>
<span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> AWS.S3({
    <span class="hljs-attr">endpoint</span>: <span class="hljs-string">`contabo_storage_bucket_url`</span>, <span class="hljs-comment">// e.g. https://eu2.contabostorage.com/bucketname</span>
    <span class="hljs-attr">accessKeyId</span>: <span class="hljs-string">"your_access_key_here"</span>,
    <span class="hljs-attr">secretAccessKey</span>: <span class="hljs-string">"your_secret_key_here"</span>,
    <span class="hljs-attr">s3BucketEndpoint</span>: <span class="hljs-literal">true</span>,
});

<span class="hljs-comment">// Middleware to handle incoming files</span>
app.use(fileUpload({
    <span class="hljs-attr">createParentPath</span>: <span class="hljs-literal">true</span>
}))

<span class="hljs-comment">// Show the form</span>
app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    response.sendFile(__dirname + <span class="hljs-string">"/form.html"</span>)
})

<span class="hljs-comment">// Handle files</span>
app.post(<span class="hljs-string">"/upload"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    <span class="hljs-keyword">const</span> file = request?.files?.[<span class="hljs-string">"fileToUpload"</span>] || <span class="hljs-literal">null</span>

    <span class="hljs-comment">// Return if the request doesn't contain the file</span>
    <span class="hljs-keyword">if</span>(!file) <span class="hljs-keyword">return</span> response.sendStatus(<span class="hljs-number">400</span>)

    <span class="hljs-comment">// Destructure the content of the file object</span>
    <span class="hljs-keyword">const</span> { name, mimetype, size, data } = file
    <span class="hljs-keyword">const</span> fileContent  = Buffer.from(data, <span class="hljs-string">' '</span>);

    <span class="hljs-comment">/* Add security checks (e.g. max size) here */</span>

    s3.putObject({
        <span class="hljs-attr">Body</span>: fileContent,
        <span class="hljs-attr">Bucket</span>: <span class="hljs-string">"your_bucket"</span>,
        <span class="hljs-attr">Key</span>: name,
    }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, data</span>) </span>{
        <span class="hljs-built_in">console</span>.log(err, data)
        <span class="hljs-keyword">if</span> (err) {
            response.sendStatus(<span class="hljs-number">500</span>)
        } <span class="hljs-keyword">else</span> {
            response.sendStatus(<span class="hljs-number">200</span>)
        }
    });
})

<span class="hljs-comment">// Show all files</span>
app.get(<span class="hljs-string">"/list"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{
    <span class="hljs-comment">// Get all objects inside the bucket</span>
    s3.listObjects({
        <span class="hljs-attr">Bucket</span>: <span class="hljs-string">"your_bucket"</span>,
    }, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, data</span>) </span>{
        <span class="hljs-keyword">if</span> (err) {
            response.sendStatus(<span class="hljs-number">500</span>)
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Return the list ("Contents") as JSON</span>
            response.json(data.Contents)
        }
    })
})

app.listen(<span class="hljs-number">4000</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"🚀 App is running on http://localhost:4000"</span>)
})
</code></pre>
<hr />
<h2 id="heading-python3-and-flask">Python3 and Flask</h2>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request  <span class="hljs-comment"># pip install flask</span>
<span class="hljs-keyword">import</span> boto3  <span class="hljs-comment"># pip install boto3</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> werkzeug.utils <span class="hljs-keyword">import</span> secure_filename
app = Flask(__name__)

session = boto3.session.Session()
client = session.client(<span class="hljs-string">'s3'</span>,
                        region_name=<span class="hljs-string">'eu2'</span>,
                        <span class="hljs-comment"># e.g. https://eu2.contabostorage.com</span>
                        endpoint_url=<span class="hljs-string">'contabo_storage_bucket_url'</span>,
                        aws_access_key_id=<span class="hljs-string">'your_access_key_here'</span>,
                        aws_secret_access_key=<span class="hljs-string">'your_secret_key_here'</span>)


<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">show_form</span>():</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'form.html'</span>)  <span class="hljs-comment"># located in templates/form.html</span>


<span class="hljs-meta">@app.route('/upload', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_upload</span>():</span>
    f = request.files[<span class="hljs-string">'fileToUpload'</span>]  <span class="hljs-comment"># Get the file</span>
    filename = secure_filename(f.filename)
    f.save(filename)  <span class="hljs-comment"># Save the file temporarily</span>
    client.upload_file(filename,  <span class="hljs-comment"># Path to local file</span>
                       <span class="hljs-string">'your_bucket'</span>,  <span class="hljs-comment"># Bucket name</span>
                       filename)  <span class="hljs-comment"># Name for remote file</span>
    os.remove(filename)  <span class="hljs-comment"># Remove the file after uploading</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">'Done!'</span>


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<hr />
<p>And that's it - now you know how to upload files to your Contabo Object Storage! 🥳</p>
<p>Thank you very much for reading and don't forget to follow me <a target="_blank" href="https://twitter.com/EinLinuus">on Twitter @EinLinuus</a> and also <a target="_blank" href="https://hashnode.com/@EinLinuus">here on Hashnode</a> and get notified everytime I publish a new post. 🙌</p>
<p>Have a nice day 🚀👋</p>
]]></content:encoded></item><item><title><![CDATA[Deploy a SvelteKit-App to DigitalOcean]]></title><description><![CDATA[Hii 👋
here on my blog and on Twitter, I've said very often that SvelteKit is awesome. So in this guide, I'll show you how you can deploy and host your SvelteKit-App.
Requirements
To follow along, you obviously need a SvelteKit-App you want to deploy...]]></description><link>https://linu.us/deploy-a-sveltekit-app-to-digitalocean</link><guid isPermaLink="true">https://linu.us/deploy-a-sveltekit-app-to-digitalocean</guid><category><![CDATA[Svelte]]></category><category><![CDATA[DigitalOcean]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Mon, 07 Feb 2022 23:00:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885720922/vUYNI5Pki.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hii 👋</p>
<p>here on my <a target="_blank" href="https://linu.us">blog</a> and on <a target="_blank" href="https://twitter.com/EinLinuus">Twitter</a>, I've said very often that SvelteKit is awesome. So in this guide, I'll show you how you can deploy and host your SvelteKit-App.</p>
<h2 id="heading-requirements">Requirements</h2>
<p>To follow along, you obviously need a SvelteKit-App you want to deploy. If you don't have one, I've already written an article on <a target="_blank" href="https://linu.us/sveltekit-with-tailwindcss-v3">how to set up SvelteKit</a>.</p>
<p>There are multiple ways you can create an app on the DigitalOcean App Platform. You need to choose between one of these sources:</p>
<ul>
<li>GitHub</li>
<li>GitLab</li>
<li>Docker Hub</li>
<li>DigitalOcean Container Registry</li>
</ul>
<p>I've only tested it with GitHub and that's what I'm using in this demo. To follow along, I assume you have your SvelteKit-App stored in a GitHub repository.</p>
<h2 id="heading-setup-node">Setup Node</h2>
<p>By default, the app Platform uses NodeJS version 12. You can change the NodeJS version in the <code>package.json</code> file. I'll change the version to NodeJS 16.13.1.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"engines"</span>: {
    <span class="hljs-attr">"node"</span>: <span class="hljs-string">"16.13.1"</span>
  },
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"..."</span>
}
</code></pre>
<h2 id="heading-expose-the-host">Expose the host</h2>
<p>In order for your app to be accessible, the host must be exposed. We can do this with a simple <code>--host</code> flag on the <code>preview</code> command in the <code>package.json</code>:</p>
<pre><code>{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"svelte-kit dev"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"svelte-kit build"</span>,
    <span class="hljs-attr">"package"</span>: <span class="hljs-string">"svelte-kit package"</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"HOST=0.0.0.0 svelte-kit preview --host"</span>,
  },
}
</code></pre><h2 id="heading-setup-the-app">Setup the App</h2>
<p>If you're ready to deploy, head over to DigitalOcean and <a target="_blank" href="https://cloud.digitalocean.com/apps/new">create a new app</a>. In the source selection, select the source where your app's source code is hosted.</p>
<p>In the next step, you'll be asked for a unique name for your app. Also, you need to choose the branch that'll be used as the source for the deployments. If you push an update to this branch, the app will re-deploy automatically (you can disable this later).</p>
<p>Next up, choose "Web service" as the type for the component (one app can have multiple components. The SvelteKit-App is one component). Settings:</p>
<ul>
<li>HTTP Routes / Path: <code>/</code></li>
<li>Build command: <code>npm run build</code></li>
<li>Run command: <code>npm run preview</code></li>
<li>HTTP Port: <code>3000</code></li>
</ul>
<p>You can also add environment variables here if you want (can be changed later).</p>
<p>After this configuration, move on to the plan selection. Make sure you're using at least the Basic-Plan because in order for our SvelteKit-App to work we need the "dynamic-apps" feature.</p>
<p>If you're just getting started, the smallest container size for 5$ / month is most likely enough for you.</p>
<p>Now you can hit launch and wait for the deployment process to be finished.</p>
<h2 id="heading-more-configuration">More configuration</h2>
<p>Here are some settings you might want to change:</p>
<ul>
<li>Environment variables: Settings -&gt; App / Component -&gt; Environment variables (choose "App" to make them available to all components or select only one component to make them only available to the specified component)</li>
<li>Disable auto-deploy: Settings -&gt; Component -&gt; Source</li>
</ul>
<hr />
<p>And that's it for this article - I hope this helped you and you can now deploy your SvelteKit-App easy and fast 🔥</p>
<p>Thank you so much for reading and I'll see you in the next one 👋</p>
]]></content:encoded></item><item><title><![CDATA[Live-Chat with SvelteKit and SocketIO]]></title><description><![CDATA[Hi! 👋
Today we're taking a look at Socket.IO and how we can integrate it into our SvelteKit project. As an example, we'll be building a realtime chat app in this demo.
Setup the project
First, we need a SvelteKit project. If you already have a proje...]]></description><link>https://linu.us/live-chat-with-sveltekit-and-socketio</link><guid isPermaLink="true">https://linu.us/live-chat-with-sveltekit-and-socketio</guid><category><![CDATA[Svelte]]></category><category><![CDATA[SocketIO]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sun, 06 Feb 2022 20:10:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885677878/zHviB5JmZ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi! 👋</p>
<p>Today we're taking a look at Socket.IO and how we can integrate it into our SvelteKit project. As an example, we'll be building a realtime chat app in this demo.</p>
<h2 id="heading-setup-the-project">Setup the project</h2>
<p>First, we need a SvelteKit project. If you already have a project, you can skip this step. If not, you can simply create one with the following commands:</p>
<pre><code class="lang-bash">npm init svelte@next live-chat-app
<span class="hljs-built_in">cd</span> live-chat-app
npm install
npm run dev -- --open
</code></pre>
<p>I'm using Tailwind CSS in this demo. I've already written an article on <a target="_blank" href="https://linu.us/sveltekit-with-tailwindcss-v3">how to add Tailwind CSS to SvelteKit</a>.</p>
<h2 id="heading-install-the-dependencies">Install the dependencies</h2>
<p>All we need is Socket.IO for the server-side part (handling the incoming requests and connections) and the Socket.IO-Client for the client-side part (sending and receiving messages).</p>
<pre><code class="lang-bash">npm i socket.io socket.io-client
</code></pre>
<h2 id="heading-inject-the-socketio-server">Inject the Socket.IO server</h2>
<p>We can inject the Socket.IO server directly in the SvelteKit config:</p>
<pre><code class="lang-js"><span class="hljs-comment">// ... (other imports here)</span>
<span class="hljs-keyword">import</span> { Server } <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io'</span>; <span class="hljs-comment">// &lt;-- Import the Socket.IO server</span>

<span class="hljs-keyword">const</span> config = {
    <span class="hljs-comment">// ...</span>

    <span class="hljs-attr">kit</span>: {
        <span class="hljs-attr">adapter</span>: adapter(),
        <span class="hljs-attr">vite</span>: {
            <span class="hljs-attr">plugins</span>: [
                {
                    <span class="hljs-attr">name</span>: <span class="hljs-string">'sveltekit-socket-io'</span>,
                    configureServer(server) {
                        <span class="hljs-keyword">const</span> io = <span class="hljs-keyword">new</span> Server(server.httpServer);

                        <span class="hljs-comment">// Socket.IO stuff goes here                </span>

                        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'SocketIO injected'</span>);
                    }
                }
            ]
        }
    },

    <span class="hljs-comment">// ...</span>
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> config;
</code></pre>
<h2 id="heading-server-side-part">Server-side part</h2>
<p>For this demo, I'll keep things simple and generate a random username for each client and just broadcast incoming messages along with the username and time.</p>
<pre><code class="lang-js"><span class="hljs-comment">// This is located in the svelte config (see above "Socket.IO stuff goes here")</span>
io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function">(<span class="hljs-params">socket</span>) =&gt;</span> {
    <span class="hljs-comment">// Generate a random username and send it to the client to display it</span>
    <span class="hljs-keyword">let</span> username = <span class="hljs-string">`User <span class="hljs-subst">${<span class="hljs-built_in">Math</span>.round(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">999999</span>)}</span>`</span>;
    socket.emit(<span class="hljs-string">'name'</span>, username);

    <span class="hljs-comment">// Receive incoming messages and broadcast them</span>
    socket.on(<span class="hljs-string">'message'</span>, <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
        io.emit(<span class="hljs-string">'message'</span>, {
            <span class="hljs-attr">from</span>: username,
            <span class="hljs-attr">message</span>: message,
            <span class="hljs-attr">time</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleString()
        });
    });
});
</code></pre>
<p>Please note: This guide is not a full Socket.IO guide. The example is pretty simple and only to show you how to use Socket.IO with SvelteKit.</p>
<h2 id="heading-client-side-part">Client-Side part</h2>
<h3 id="heading-connect-to-socketio">Connect to Socket.IO</h3>
<p>Because you may want to use the Socket.IO connection in multiple components or pages, I recommend to separate the connection stuff:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/lib/realtime.js</span>
<span class="hljs-keyword">import</span> ioClient <span class="hljs-keyword">from</span> <span class="hljs-string">"socket.io-client"</span>;
<span class="hljs-keyword">const</span> ENDPOINT = <span class="hljs-string">"http://localhost:3000"</span>;

<span class="hljs-keyword">const</span> socket = ioClient(ENDPOINT)

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> io = socket
</code></pre>
<p>Now we can import and use <code>io</code> everywhere in our project.</p>
<h3 id="heading-layout">Layout</h3>
<p>Before we add Socket.IO to the client-side, I'll create a simple UI for our demo. Because this is only the chat, I'll do this directly in the <code>src/routes/index.svelte</code>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">import</span> { onMount } <span class="hljs-keyword">from</span> <span class="hljs-string">"svelte"</span>;

    <span class="hljs-keyword">let</span> textfield = <span class="hljs-string">""</span>
    <span class="hljs-keyword">let</span> username = <span class="hljs-string">""</span>

    <span class="hljs-keyword">let</span> messages = []

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params"></span>) </span>{

    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-screen w-screen bg-zinc-800"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-full w-full max-w-md mx-auto bg-zinc-500 flex flex-col"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 border-b border-zinc-800 bg-zinc-700 text-white shrink-0 flex items-center justify-between"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-bold text-xl"</span>&gt;</span>My Chat app<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{username}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-full w-full p-4"</span>&gt;</span>
            {#each messages as message}
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-zinc-300 rounded-xl rounded-tl-none px-4 py-3 my-4 w-fit"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center space-between gap-4"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>{message.from}<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">i</span>&gt;</span>{message.time}<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    {message.message}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            {/each}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">on:submit</span>|<span class="hljs-attr">preventDefault</span>=<span class="hljs-string">{sendMessage}</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 border-t border-zinc-800 bg-zinc-700 text-white shrink-0 flex items-center"</span>
        &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">bind:value</span>=<span class="hljs-string">{textfield}</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Type something..."</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-none px-4 py-3 w-full"</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shrink-0 border border-white rounded-lg px-4 py-3"</span>&gt;</span>Send<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>As you can see, I'm storing all messages inside the <code>messages</code> array and output them inside the <code>each</code> loop.</p>
<p>To send messages, I've attached a listener to the form <code>sendMessage</code>.</p>
<h3 id="heading-send-messages">Send messages</h3>
<p>First, we need to import <code>io</code> from the file we just created (realtime.js).
In our send function, we can simply trigger the <code>message</code> event (that's how we called it in the server-side part) with the message:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">import</span> { io } <span class="hljs-keyword">from</span> <span class="hljs-string">"$lib/realtime"</span>;
    <span class="hljs-keyword">import</span> { onMount } <span class="hljs-keyword">from</span> <span class="hljs-string">"svelte"</span>;

    <span class="hljs-keyword">let</span> textfield = <span class="hljs-string">""</span>
    <span class="hljs-keyword">let</span> username = <span class="hljs-string">""</span>

    <span class="hljs-keyword">let</span> messages = []

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> message = textfield.trim()
        <span class="hljs-keyword">if</span>(!message) <span class="hljs-keyword">return</span>

        textfield = <span class="hljs-string">""</span>
        io.emit(<span class="hljs-string">"message"</span>, message) <span class="hljs-comment">// Send the message</span>
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-receive-messages">Receive messages</h3>
<p>To receive messages, we need to listen to the <code>message</code> event (that's how we called it in the server-side part). The object we receive is the same object we sent (server-side) and we can just add it to the <code>messages</code> array:</p>
<pre><code class="lang-js">&lt;script lang=<span class="hljs-string">"ts"</span>&gt;
    <span class="hljs-keyword">import</span> { io } <span class="hljs-keyword">from</span> <span class="hljs-string">"$lib/realtime"</span>;
    <span class="hljs-keyword">import</span> { onMount } <span class="hljs-keyword">from</span> <span class="hljs-string">"svelte"</span>;

    <span class="hljs-keyword">let</span> textfield = <span class="hljs-string">""</span>
    <span class="hljs-keyword">let</span> username = <span class="hljs-string">""</span>

    <span class="hljs-keyword">let</span> messages = []

    onMount(<span class="hljs-function">() =&gt;</span> {
        io.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">message</span> =&gt;</span> { <span class="hljs-comment">// Listen to the message event</span>
            messages = [...messages, message]
        })
        io.on(<span class="hljs-string">"name"</span>, <span class="hljs-function"><span class="hljs-params">name</span> =&gt;</span> { <span class="hljs-comment">// Another listener for the name:</span>
            username = name <span class="hljs-comment">// Update the name so it can be displayed</span>
        })
    })

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> message = textfield.trim()
        <span class="hljs-keyword">if</span>(!message) <span class="hljs-keyword">return</span>

        textfield = <span class="hljs-string">""</span>
        io.emit(<span class="hljs-string">"message"</span>, message) <span class="hljs-comment">// Send the message</span>
    }
&lt;/script&gt;
</code></pre>
<hr />
<p>Now, run <code>npm run dev</code> and test it out:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644177870810/Zi_E7t660.png" alt="image.png" /></p>
<hr />
<h2 id="heading-make-it-work-in-production">Make it work in production</h2>
<p>The configuration above works perfectly in development environments (npm run dev), but didn't really work in production environments, so here is a quick guide on how to make it work in both development and production environments:</p>
<p>Make sure you use the <code>adapter-node</code> and create a <code>server.js</code> file. Here we can create our own express server and add SvelteKit to it using the handlers in the <code>build</code> directory (created by the adapter-node). In here, we can then inject SocketIO:</p>
<pre><code class="lang-js"><span class="hljs-comment">// server.js</span>
<span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">"http"</span>;
<span class="hljs-keyword">import</span> { handler } <span class="hljs-keyword">from</span> <span class="hljs-string">'./build/handler.js'</span>; <span class="hljs-comment">// &lt;- Import SvelteKit handlers</span>
<span class="hljs-keyword">import</span> injectSocketIO <span class="hljs-keyword">from</span> <span class="hljs-string">"./socket-handler.js"</span>; <span class="hljs-comment">// The SocketIO stuff (see next step)</span>
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> server = http.createServer(app);

<span class="hljs-comment">// Inject SocketIO</span>
injectSocketIO(server)

<span class="hljs-comment">// SvelteKit handlers</span>
app.use(handler);

server.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Running on http://localhost:3000'</span>);
});
</code></pre>
<p>To eliminate code duplication, extract the SocketIO stuff from the <code>sveltekit.config.js</code> to a new file <code>socket-handler.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-comment">// socket-handler.js</span>
<span class="hljs-keyword">import</span> { Server } <span class="hljs-keyword">from</span> <span class="hljs-string">"socket.io"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">injectSocketIO</span>(<span class="hljs-params">server</span>) </span>{
    <span class="hljs-keyword">const</span> io = <span class="hljs-keyword">new</span> Server(server);

    <span class="hljs-comment">// Socket.IO stuff goes here</span>
    io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function">(<span class="hljs-params">socket</span>) =&gt;</span> {
        <span class="hljs-comment">// Generate a random username and send it to the client to display it</span>
        <span class="hljs-keyword">let</span> username = <span class="hljs-string">`User <span class="hljs-subst">${<span class="hljs-built_in">Math</span>.round(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">999999</span>)}</span>`</span>;
        socket.emit(<span class="hljs-string">'name'</span>, username);

        <span class="hljs-comment">// Receive incoming messages and broadcast them</span>
        socket.on(<span class="hljs-string">'message'</span>, <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
            io.emit(<span class="hljs-string">'message'</span>, {
                <span class="hljs-attr">from</span>: username,
                <span class="hljs-attr">message</span>: message,
                <span class="hljs-attr">time</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleString()
            });
        });
    });

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'SocketIO injected'</span>);
}
</code></pre>
<p>And now use this function in the SvelteKit config as well so it works in both dev and production environments:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> adapter <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/adapter-node'</span>;
<span class="hljs-keyword">import</span> injectSocketIO <span class="hljs-keyword">from</span> <span class="hljs-string">"./socket-handler.js"</span>; <span class="hljs-comment">// &lt;- Import the new function</span>

<span class="hljs-comment">/** @type {import('@sveltejs/kit').Config} */</span>
<span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">kit</span>: {
        <span class="hljs-attr">adapter</span>: adapter(),
        <span class="hljs-attr">vite</span>: {
            <span class="hljs-attr">plugins</span>: [
                {
                    <span class="hljs-attr">name</span>: <span class="hljs-string">"sveltekit-socket-io"</span>,
                    configureServer(server) {
                        injectSocketIO(server.httpServer); <span class="hljs-comment">// &lt;- call the function here</span>
                    }
                }
            ]
        }
    }
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> config;
</code></pre>
<p>Now, to run the server:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Dev</span>
npm run dev

<span class="hljs-comment"># Production</span>
npm run build
node server.js
</code></pre>
<hr />
<p>And that's it - now you can send data in realtime between the server and the client! 🚀</p>
<p>Thanks for reading and have a great day 🤗</p>
]]></content:encoded></item><item><title><![CDATA[SQL: The basics and beyond]]></title><description><![CDATA[Welcome back 👋
Today we'll take a look at SQL, starting with the basics and then moving forwards to more complicated concepts and queries. But before we start writing the first query, make sure you're familiar with the core concepts of SQL and relat...]]></description><link>https://linu.us/sql-the-basics-and-beyond</link><guid isPermaLink="true">https://linu.us/sql-the-basics-and-beyond</guid><category><![CDATA[SQL]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Tue, 01 Feb 2022 15:04:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885632362/kBKhNDd-G.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back 👋</p>
<p>Today we'll take a look at SQL, starting with the basics and then moving forwards to more complicated concepts and queries. But before we start writing the first query, make sure you're familiar with the core concepts of SQL and relational databases. Making queries is important, but knowing how to structure a database even more.</p>
<h2 id="heading-what-is-sql">What is SQL?</h2>
<p>First things first, what the hell is SQL? If you don't know already, SQL stands for "Structured Query Language" and is a database query language. It's used to interact with databases like MySQL, Postgres or SQLite. The data in relational databases is organized in tables and rows.</p>
<h3 id="heading-how-the-data-is-organized">How the data is organized</h3>
<p>You store your data in a database that can contain many tables. For example, the database "blog" contains the tables "users" and "posts". Each table has many columns. The "posts" table may have the columns "title", "image" and "description".</p>
<h3 id="heading-relations">Relations</h3>
<p>In relational databases, there are mostly no duplications. If we use the example above, we don't store the user information in the "posts" table, because one user can have multiple posts, which means the user information would be stored multiple times in the table. Instead, we store the user information in a separate table "users" and reference the user in the "posts" table.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643558682877/zT8TYBiWw.png" alt="image.png" /></p>
<h3 id="heading-primary-and-foreign-keys">Primary and foreign keys</h3>
<p>In order to reference something, we need a unique identifier of the row we want to reference.</p>
<blockquote>
<p>Note: Using something like the username may seems logical, but I highly recommend to use a separate, unique ID for each user that <strong>can't be changed</strong>. A username is unique, but if a user changes the username, you'd need to change the username <strong>everywhere you referenced it</strong>.</p>
</blockquote>
<p>This unique identifier is called a primary key. When you use it as a reference, for example in the "posts" table, it's called a foreign key.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643558801573/mVC1cvOEv.png" alt="image.png" /></p>
<p>Conclusion: A foreign key references a primary key in another table that's unique for that table.</p>
<hr />
<h2 id="heading-create-the-database">Create the database</h2>
<p>To get started, we first need a database. You can install for example MySQL on your local machine, if available on a server or rent a database online.</p>
<p>But if you're just starting and want to follow along with this guide, you can use my SQL playground, based on SQLite:</p>
<p><a target="_blank" href="https://sql.webaze.net/">Open the SQL-Playground on sql.webaze.net</a></p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> <span class="hljs-keyword">users</span> (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">255</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    username <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">255</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    PRIMARY <span class="hljs-keyword">KEY</span> (<span class="hljs-keyword">id</span>)
);
</code></pre>
<p>Executing this query will create a database called <code>users</code> and the columns</p>
<ul>
<li><code>id</code> (Integer) which is the primary key (see <code>PRIMARY KEY (id)</code>) and can't be null</li>
<li><code>name</code> which is a varchar/string with a max length of 255, can't be null</li>
<li><code>username</code> which is also a varchar/string with a max length of 255, can't be null</li>
</ul>
<p>To prevent errors if the table already exists, I used <code>CREATE TABLE IF NOT EXISTS</code> instead of just <code>CREATE TABLE</code>.</p>
<hr />
<h2 id="heading-insert-data">Insert data</h2>
<p>Now we have the database and table ready, let's create our first user. Insertions are made with the <code>INSERT</code> statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">users</span> (<span class="hljs-keyword">id</span>, <span class="hljs-keyword">name</span>, username) <span class="hljs-keyword">VALUES</span> (<span class="hljs-number">1</span>, <span class="hljs-string">"Linus"</span>, <span class="hljs-string">"EinLinuus"</span>);
</code></pre>
<p>We can also insert multiple rows at once:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span>
    <span class="hljs-keyword">users</span> (<span class="hljs-keyword">id</span>, <span class="hljs-keyword">name</span>, username)
<span class="hljs-keyword">VALUES</span>
    (<span class="hljs-number">1</span>, <span class="hljs-string">"Linus"</span>, <span class="hljs-string">"EinLinuus"</span>),
    (<span class="hljs-number">2</span>, <span class="hljs-string">"John"</span>, <span class="hljs-string">"John_Doe"</span>);
</code></pre>
<p>This should give you the following result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643559980052/uFtGDpnin.png" alt="image.png" /></p>
<hr />
<h2 id="heading-query-the-users">Query the users</h2>
<p>With a few rows in our table, we can now query them. To select data from the database, use the <code>SELECT</code> statement:</p>
<pre><code class="lang-sql"><span class="hljs-comment">/* Select everything (*) */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;

<span class="hljs-comment">/* Select only specific columns */</span>
<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">name</span>, username <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643560292006/3iJ1EMsp_.png" alt="image.png" /></p>
<h3 id="heading-filtering">Filtering</h3>
<p>Getting all users at once may be useful in some cases, but most often you want to filter them, for example get a specific user by the username or search through them with a search keyword.</p>
<pre><code class="lang-sql"><span class="hljs-comment">/* Get all users with the username "EinLinuus"  */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> username = <span class="hljs-string">"EinLinuus"</span>;

<span class="hljs-comment">/* Get the user with the id 2 */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">2</span>;

<span class="hljs-comment">/* Combinations with AND and OR */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">1</span> <span class="hljs-keyword">AND</span> <span class="hljs-keyword">name</span> = <span class="hljs-string">"Linus"</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643560634948/mmi_fkynx.png" alt="image.png" /></p>
<p>There are many operators you can use here:</p>
<pre><code class="lang-sql"><span class="hljs-comment">/* is not */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> username != <span class="hljs-string">"EinLinuus"</span>;

<span class="hljs-comment">/* LIKE (% stands for anything) */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> username <span class="hljs-keyword">LIKE</span> <span class="hljs-string">"%Linuus%"</span>;

<span class="hljs-comment">/* Greater / Less than */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> &gt; <span class="hljs-number">1</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643560813914/4mtm2EQpl.png" alt="image.png" /></p>
<h3 id="heading-limits-and-pagination">Limits and pagination</h3>
<p>To limit or offset your results, use <code>LIMIT</code>. You can use it simply limit the amount of rows returned, or to offset it (return X rows, but skip the first Y rows).</p>
<pre><code class="lang-sql"><span class="hljs-comment">/* Return only 1 row */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">1</span>;

<span class="hljs-comment">/* Return only 5 rows and skip 1 row */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">1</span>, <span class="hljs-number">5</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643561167405/5FRh841J1.png" alt="image.png" /></p>
<h3 id="heading-ordering">Ordering</h3>
<p>Ordering is possible too, just use <code>ORDER BY</code> followed by the column name and the direction.</p>
<pre><code class="lang-sql"><span class="hljs-comment">/* Order ascending by id */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">id</span> <span class="hljs-keyword">ASC</span>;

<span class="hljs-comment">/* Order descending by id */</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">id</span> <span class="hljs-keyword">DESC</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643561485190/xT_aHC2op.png" alt="image.png" /></p>
<p>You can also order by multiple columns:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span>
   <span class="hljs-keyword">name</span> <span class="hljs-keyword">DESC</span>,
   <span class="hljs-keyword">id</span> <span class="hljs-keyword">ASC</span>;
</code></pre>
<h3 id="heading-counting">Counting</h3>
<p>Now if your multi-billion startup idea starts getting users, you might want to know how many users exactly:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">id</span>) <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643562251878/Tm1xUFAFk.png" alt="image.png" /></p>
<p>This will return one row with the count. But, the column name in the result is <code>COUNT(id)</code>. You might want to have something more readable here, and that's why you can...</p>
<h3 id="heading-rename-columns">Rename columns</h3>
<p>To rename a column, just use <code>AS</code> followed by the new name:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">id</span> <span class="hljs-keyword">AS</span> user_id, username <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;
<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">id</span>) <span class="hljs-keyword">as</span> total_users <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643562236935/bCgj-xVJd.png" alt="image.png" /></p>
<h3 id="heading-grouping">Grouping</h3>
<p>Grouping allows you to group multiple rows, for example we can get the amount of users with the same name:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">id</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">users</span>, <span class="hljs-keyword">name</span>
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">name</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643562534872/y2a1gSc6-.png" alt="image.png" /></p>
<h3 id="heading-ordering-inside-the-query">Ordering inside the query</h3>
<p>If you want to combine multiple of the above, make sure you respect this order:</p>
<ol>
<li><code>SELECT</code> Which columns you want to select</li>
<li><code>FROM</code> Which table you want to query</li>
<li><code>WHERE</code> Which rows you want to select</li>
<li><code>GROUP</code> How you want to group the rows</li>
<li><code>ORDER</code> In which order you want the result</li>
<li><code>LIMIT</code> How many rows you want</li>
</ol>
<hr />
<h2 id="heading-update-rows">Update rows</h2>
<p>Updating rows is just as easy as inserting new ones. Use <code>UPDATE</code> to update a row and use <code>WHERE</code> and <code>LIMIT</code> from the <code>SELECT</code> guide to specify which rows you want to update.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">SET</span> username = <span class="hljs-string">"JohnDoe"</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">2</span>;

<span class="hljs-comment">/* Update multiple columns */</span>
<span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">SET</span> <span class="hljs-keyword">name</span>= <span class="hljs-string">"John D"</span>, username = <span class="hljs-string">"JohnDoe"</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">2</span>;
</code></pre>
<p>The query above changes the username user 2 to "JohnDoe".</p>
<hr />
<h2 id="heading-delete-rows">Delete rows</h2>
<p>Deleting works nearly the same, with the only difference that you just specify a table, because you're deleting the whole row.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">2</span>;
</code></pre>
<hr />
<h2 id="heading-work-with-multiple-tables">Work with multiple tables</h2>
<p>Things get much more interesting when we work with multiple tables. Let's add another table to our example:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> posts (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    title <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">255</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">user</span> <span class="hljs-built_in">INT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    PRIMARY <span class="hljs-keyword">KEY</span> (<span class="hljs-keyword">id</span>)
);

<span class="hljs-comment">/* Insert some dummy data */</span>
<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> posts (<span class="hljs-keyword">id</span>, title, <span class="hljs-keyword">user</span>) <span class="hljs-keyword">VALUES</span>
(<span class="hljs-number">1</span>, <span class="hljs-string">"Post 1"</span>, <span class="hljs-number">1</span>),
(<span class="hljs-number">2</span>, <span class="hljs-string">"Post 2"</span>, <span class="hljs-number">2</span>),
(<span class="hljs-number">3</span>, <span class="hljs-string">"Post 3"</span>, <span class="hljs-number">2</span>),
(<span class="hljs-number">4</span>, <span class="hljs-string">"Post 4"</span>, <span class="hljs-number">1</span>);
</code></pre>
<p>If you followed along, you know how to get all posts, filter, order, update and delete them. But you may have noticed the connection between both tables:
The <code>posts</code> table is referencing users from the <code>users</code> table in the <code>posts.user</code> column.</p>
<p>Let's say we want to display all posts on a website. We create a simple <code>SELECT</code> statement to query all available posts:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> posts;
</code></pre>
<p>This works, but we want to show the author's name as well. How can we do that?</p>
<h3 id="heading-table-joins">Table joins</h3>
<p>Inside one SQL query, we can join other tables to access their data as well.</p>
<p>There are different types of joins:</p>
<p>A <code>INNER</code> join connects all rows that are in both tables. If a user has no posts, that user  gets skipped.</p>
<p>A <code>LEFT</code> join uses the first table as basis and connects, if possible, the second table.
The <code>RIGHT</code> join is basically the opposite of a left join, here is the second table the basis.</p>
<p>A <code>FULL</code> / <code>FULL OUTER</code> join connects both tables and missing data is represented as <code>NULL</code>.</p>
<p>Now, let's create a query with a JOIN to get all posts and their authors:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">JOIN</span> posts <span class="hljs-keyword">ON</span> posts.user = users.id;
</code></pre>
<p>The <code>ON field = otherField</code> part tells the database which row(s) to select in the table we're joining. In this case, the column <code>user</code> in the <code>posts</code> table references a <code>id</code> in the <code>users</code> table.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643566673720/fRc-ec77b.png" alt="image.png" /></p>
<p>We can also use the data from the other table inside our WHERE clause:</p>
<pre><code class="lang-sql"><span class="hljs-comment">/* posts.* = Everything from the posts table */</span>
<span class="hljs-keyword">SELECT</span> posts.* <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">JOIN</span> posts <span class="hljs-keyword">ON</span> posts.user = users.id
<span class="hljs-keyword">WHERE</span> users.name = <span class="hljs-string">"EinLinuus"</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643566910277/Bk-ixGls8.png" alt="image.png" /></p>
<hr />
<h2 id="heading-beyond-this">Beyond this</h2>
<p>SQL is very powerful and can do much more than what I described in this article. Because it's a lot of work to write something like this, you can find the next part of this guide on my BMC-Page. Thank you very much ❤️</p>
<p><strong>Content of Part 2:</strong></p>
<ol>
<li>Relations: One-to-Many and Many-to-Many</li>
<li>Working with dates (MySQL)</li>
<li>The check constraint</li>
<li>Working with tables<ol>
<li>Edit tables / Alter</li>
<li>Delete tables / Drop</li>
</ol>
</li>
<li>Security: SQL Injections</li>
</ol>
<p><a target="_blank" href="https://www.buymeacoffee.com/EinLinuus/sql-the-full-guide">SQL: The basics and beyond (Part 2)</a></p>
<hr />
<p>Thanks for reading 🤗</p>
<div class="hn-embed-widget" id="bmac"></div>]]></content:encoded></item><item><title><![CDATA[SvelteKit with Tailwind CSS v3]]></title><description><![CDATA[Tailwind CSS version 3 launched just a few days ago, so now you can use it in your projects! If you're using SvelteKit - this article is a simple guide so you can add Tailwind CSS v3 to your project.
Don't worry - you don't need to have an existing S...]]></description><link>https://linu.us/sveltekit-with-tailwindcss-v3</link><guid isPermaLink="true">https://linu.us/sveltekit-with-tailwindcss-v3</guid><category><![CDATA[Svelte]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[#howtos]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[guide]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sun, 12 Dec 2021 22:53:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885535165/wbRXfrnNW.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://tailwindcss.com/blog/tailwindcss-v3">Tailwind CSS version 3</a> launched just a few days ago, so now you can use it in your projects! If you're using <a target="_blank" href="https://kit.svelte.dev/">SvelteKit</a> - this article is a simple guide so you can add Tailwind CSS v3 to your project.</p>
<p>Don't worry - you don't need to have an existing SvelteKit project - we'll start at the beginning :)</p>
<blockquote>
<p>Don't want to read, just code? <a target="_blank" href="https://github.com/EinLinuus/sveltekit-tailwindcss-template">Click here to use the template</a></p>
</blockquote>
<h2 id="heading-1-setup-a-sveltekit-project">1. Setup a SvelteKit project</h2>
<p>First up, we need a SvelteKit project. If you already have one, you can skip this step.</p>
<p>The setup is done by a few simple commands:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Setup</span>
npm init svelte@next my-app
<span class="hljs-comment"># Navigate into the project's folder</span>
<span class="hljs-built_in">cd</span> my-app
<span class="hljs-comment"># Install the dependencies</span>
npm install
</code></pre>
<h2 id="heading-2-install-the-dependencies">2. Install the dependencies</h2>
<p>We'll install the following dependencies:</p>
<ul>
<li><strong><a target="_blank" href="https://tailwindcss.com/">tailwindcss</a></strong> (obviously)</li>
<li><strong>postcss</strong> because we use TailwindCSS as a PostCSS plugin</li>
<li><strong>cssnano</strong> to optimize the CSS</li>
<li><strong>autoprefixer</strong> to add prefixes to CSS-properties for better browser-support</li>
<li><strong>svelte-preprocess</strong> to run PostCSS as a preprocessor</li>
</ul>
<p>And (optional) these first-party Tailwind CSS plugins:</p>
<ul>
<li><strong><a target="_blank" href="https://github.com/tailwindlabs/tailwindcss-forms">forms</a></strong> to make form-styling more easy</li>
<li><strong><a target="_blank" href="https://github.com/tailwindlabs/tailwindcss-typography">typography</a></strong> to style vanilla HTML</li>
</ul>
<p>Because we need them to preprocess our CSS and not in the production build, we can save them as development dependencies with the <code>-D</code> flag.</p>
<pre><code class="lang-bash">npm i tailwindcss postcss cssnano autoprefixer svelte-preprocess @tailwindcss/forms @tailwindcss/typography -D
</code></pre>
<h2 id="heading-3-configure-postcss">3. Configure PostCSS</h2>
<p>Now we have the dependencies installed, we need to configure them correctly. Create a file <code>postcss.config.cjs</code> in the project's root folder and add this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> tailwindcss = <span class="hljs-built_in">require</span>(<span class="hljs-string">"tailwindcss"</span>)
<span class="hljs-keyword">const</span> autoprefixer = <span class="hljs-built_in">require</span>(<span class="hljs-string">"autoprefixer"</span>)
<span class="hljs-keyword">const</span> cssnano = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cssnano"</span>)

<span class="hljs-keyword">const</span> mode = process.env.NODE_ENV
<span class="hljs-keyword">const</span> dev = mode === <span class="hljs-string">"development"</span>

<span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">plugins</span>: [
        tailwindcss(), <span class="hljs-comment">// first load Tailwind CSS</span>
        autoprefixer(), <span class="hljs-comment">// then run autoprefixer</span>
        !dev &amp;&amp; <span class="hljs-comment">// optimize the code for production</span>
            cssnano({
                <span class="hljs-attr">preset</span>: <span class="hljs-string">"default"</span>,
            }),
    ],
}

<span class="hljs-built_in">module</span>.exports = config
</code></pre>
<h2 id="heading-4-configure-tailwind-css">4. Configure Tailwind CSS</h2>
<p>We also need a Tailwind CSS config file, so create one called <code>tailwind.config.js</code> in the project's root folder:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">mode</span>: <span class="hljs-string">"jit"</span>,
    <span class="hljs-attr">content</span>: [<span class="hljs-string">"./src/**/*.{html,js,svelte,ts}"</span>],
    <span class="hljs-attr">theme</span>: {
        <span class="hljs-attr">extend</span>: {},
    },
    <span class="hljs-comment">// Only add this if you installed the Tailwind CSS plugins:</span>
    <span class="hljs-attr">plugins</span>: [<span class="hljs-built_in">require</span>(<span class="hljs-string">"@tailwindcss/typography"</span>), <span class="hljs-built_in">require</span>(<span class="hljs-string">"@tailwindcss/forms"</span>)],
}

<span class="hljs-built_in">module</span>.exports = config
</code></pre>
<h2 id="heading-5-add-postcss-to-sveltekit">5. Add PostCSS to SvelteKit</h2>
<p>In your <code>svelte.config.js</code>, import the installed <code>svelte-preprocess</code> module and add it to the <code>preprocess</code> array:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> adapter <span class="hljs-keyword">from</span> <span class="hljs-string">"@sveltejs/adapter-auto"</span>
<span class="hljs-keyword">import</span> preprocess <span class="hljs-keyword">from</span> <span class="hljs-string">"svelte-preprocess"</span> <span class="hljs-comment">// &lt;- Import it here</span>

<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('@sveltejs/kit').Config}</span> </span>*/</span>
<span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">preprocess</span>: [ <span class="hljs-comment">// &lt;- Create this option</span>
        preprocess({ <span class="hljs-comment">// &lt;- Add the module</span>
            <span class="hljs-attr">postcss</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// &lt;- Set this to enable PostCSS</span>
        }),
    ],
    <span class="hljs-attr">kit</span>: {
        <span class="hljs-attr">adapter</span>: adapter(),
        <span class="hljs-attr">target</span>: <span class="hljs-string">"#svelte"</span>,
    },
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> config
</code></pre>
<h2 id="heading-6-add-the-css-to-the-code">6. Add the CSS to the code</h2>
<p>We need a main CSS file, so let's create it in <code>src/app.css</code> with the following content:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>And don't forget to import the styles in your layout (so it applies to the whole project):</p>
<pre><code class="lang-js">&lt;script&gt;
    <span class="hljs-comment">// src/routes/__layout.svelte</span>
    <span class="hljs-keyword">import</span> <span class="hljs-string">"../app.css"</span>;
&lt;/script&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">slot</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">slot</span>&gt;</span></span>
</code></pre>
<h2 id="heading-test-and-enjoy">Test and enjoy</h2>
<p>That's it! You can now start your project with</p>
<pre><code class="lang-bash">npm run dev -- --open
</code></pre>
<p>and check if everything is working!</p>
<p>I hope this article helped you and you enjoy the full power of Tailwind CSS version 3 :)</p>
<p>If you didn't already, check out this awesome video from <a target="_blank" href="https://twitter.com/simonswiss">Simon Vrachliotis</a> (TailwindLabs) to see all the new features in action:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=mSC6GwizOag">https://www.youtube.com/watch?v=mSC6GwizOag</a></div>
<p>Thank you so much for reading and don't forget to follow so you don't miss new articles 🤗🚀</p>
]]></content:encoded></item><item><title><![CDATA[How to build your own CLI tool with NodeJS]]></title><description><![CDATA[We're using the command line every day (I believe), you use it when you create a commit with git, start your project in NodeJS or for any other task.
But have you every thought of building your own command for the command line?
What is a CLI
CLI stan...]]></description><link>https://linu.us/how-to-build-your-own-cli-tool-with-nodejs</link><guid isPermaLink="true">https://linu.us/how-to-build-your-own-cli-tool-with-nodejs</guid><category><![CDATA[cli]]></category><category><![CDATA[command line]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sun, 21 Nov 2021 22:11:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885487404/A3MxNyf3z.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We're using the command line every day (I believe), you use it when you create a commit with git, start your project in NodeJS or for any other task.</p>
<p>But have you every thought of building your own command for the command line?</p>
<h2 id="heading-what-is-a-cli">What is a CLI</h2>
<p>CLI stands for "Command line interface". As the name suggests, it's a way to interact with your software using the command line. Most likely, you'll have one main command, something like <code>git</code>, <code>node</code>, <code>npm</code>, and then different subcommands like <code>commit</code> or <code>push</code> for different actions. Commands may also have arguments to customize the behavior of the command.</p>
<h2 id="heading-how-to-build-your-own-cli">How to build your own CLI</h2>
<p>This won't be a complex tutorial - I'll show how to use the package <code>comander</code> to create custom commands and use arguments. The final logic of your CLI is something you need to come up with.</p>
<h3 id="heading-initialize-your-project">Initialize your project</h3>
<p>Create a new folder and run <code>npm init -y</code> to initialize a new project.</p>
<p>To build the CLI, we'll use the <a target="_blank" href="https://www.npmjs.com/package/commander">NPM package commander</a>.</p>
<pre><code class="lang-bash">npm install commander
</code></pre>
<h3 id="heading-create-the-main-file">Create the main file</h3>
<p>Entry points for CLIs are located in <code>bin</code> directories. So let's create a new file inside this folder <code>your-project/bin/index.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-meta">#!/usr/bin/env node</span>
<span class="hljs-keyword">const</span> program = <span class="hljs-built_in">require</span>(<span class="hljs-string">"commander"</span>)
</code></pre>
<p>We can also add some information to our program:</p>
<pre><code class="lang-js">program
    .version(<span class="hljs-string">"1.0.0"</span>)
    .description(<span class="hljs-string">"That's my CLI and it's doing awesome stuff"</span>)
    .parse()
</code></pre>
<h3 id="heading-the-first-command">The first command</h3>
<p>You see, it's not very complicated - so let's create our first command:</p>
<pre><code class="lang-js">program.command(<span class="hljs-string">"hello-world"</span>).action(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello :)"</span>)
})

<span class="hljs-comment">// You can also add a description</span>
program.command(<span class="hljs-string">"hello-world"</span>).description(<span class="hljs-string">"Just says hello"</span>).action(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello :)"</span>)
})
</code></pre>
<h3 id="heading-commands-with-arguments">Commands with arguments</h3>
<p>We can add arguments directly in the <code>command</code> function. You can add <code>&lt;required&gt;</code> or <code>[optional]</code> arguments:</p>
<pre><code class="lang-js">program
    .command(<span class="hljs-string">"hello &lt;name&gt;"</span>)
    .description(<span class="hljs-string">"Say hello to someone"</span>)
    .action(<span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Hello, <span class="hljs-subst">${name}</span>!`</span>)
    })
</code></pre>
<p>The arguments will be passed in the same order as in the <code>command</code> function to the <code>action</code> callback.</p>
<h3 id="heading-the-full-example">The full example</h3>
<pre><code class="lang-js"><span class="hljs-meta">#!/usr/bin/env node</span>
<span class="hljs-keyword">const</span> program = <span class="hljs-built_in">require</span>(<span class="hljs-string">"commander"</span>)

program
    .version(<span class="hljs-string">"1.0.0"</span>)
    .description(<span class="hljs-string">"That's my CLI and it's doing awesome stuff"</span>)
    .parse()

program
    .command(<span class="hljs-string">"hello-world"</span>)
    .description(<span class="hljs-string">"Just says hello"</span>)
    .action(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello :)"</span>)
    })

program
    .command(<span class="hljs-string">"hello &lt;name&gt;"</span>)
    .description(<span class="hljs-string">"Say hello to someone"</span>)
    .action(<span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Hello, <span class="hljs-subst">${name}</span>!`</span>)
    })

program.parse(process.argv)
</code></pre>
<h3 id="heading-test-the-cli">Test the CLI</h3>
<p>Before we can test the CLI, we have to add one last line at the end of the file:</p>
<pre><code class="lang-js">program.parse(process.argv)
</code></pre>
<p>This sends the arguments passed from the command line to the <code>commander</code> package that manages our commands.</p>
<p>Now we can test the CLI with the following command:</p>
<pre><code class="lang-bash">node ./bin/index.js -V
<span class="hljs-comment"># 1.0.0</span>

node ./bin/index.js hello-world
<span class="hljs-comment"># Hello :)</span>

node ./bin/index.js hello Linus
<span class="hljs-comment"># Hello, Linus!</span>

node ./bin/index.js hello <span class="hljs-string">"Linus Benkner"</span>
<span class="hljs-comment"># Hello, Linus Benkner!</span>
</code></pre>
<h2 id="heading-finish-the-cli">Finish the CLI</h2>
<p>When you're done building your CLI, let's make it ready to share it with others. Now think of a name for your command and add this to your <code>package.json</code>. For this example, I'll use <code>custom-cli-example</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"custom-cli-with-nodejs"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-attr">"bin"</span>: {
    <span class="hljs-attr">"custom-cli-example"</span>: <span class="hljs-string">"./bin/index.js"</span>
  },
  <span class="hljs-attr">"keywords"</span>: [],
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"commander"</span>: <span class="hljs-string">"^8.3.0"</span>
  }
}
</code></pre>
<p>For the final testing, let's install it on our own computer:</p>
<pre><code class="lang-bash">npm install -g
</code></pre>
<p>You can now run <code>custom-cli-example</code> with all your commands and options.</p>
<p><strong>That was pretty simple, wasn't it? 😎</strong></p>
<p>Thank you for reading and have a nice day! 🤗👋</p>
]]></content:encoded></item><item><title><![CDATA[Authentication in SvelteKit]]></title><description><![CDATA[SvelteKit - it's like NextJS, but for Svelte. In this article, I'll teach you everything you need to know about authentication with server-side rendering in SvelteKit.
This does not include things like a login with Google or database stuff - it's abo...]]></description><link>https://linu.us/authentication-in-sveltekit</link><guid isPermaLink="true">https://linu.us/authentication-in-sveltekit</guid><category><![CDATA[Svelte]]></category><category><![CDATA[authentication]]></category><category><![CDATA[Server side rendering]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sun, 21 Nov 2021 14:14:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885418373/59Vjkdq19.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://kit.svelte.dev/">SvelteKit</a> - it's like <a target="_blank" href="https://nextjs.org/">NextJS</a>, but for <a target="_blank" href="https://svelte.dev/">Svelte</a>. In this article, I'll teach you everything you need to know about authentication with server-side rendering in SvelteKit.</p>
<p>This does <strong>not</strong> include things like a login with Google or database stuff - it's about the basic realization of an authentication system with serverside rendering. We'll use a simple API endpoint in SvelteKit to allow or deny access based on a simple if-statement.</p>
<h2 id="heading-setup-a-new-sveltekit-project">Setup a new SvelteKit project</h2>
<p>Use the <code>npx</code> command to get started with a new project:</p>
<pre><code class="lang-bash">npm init svelte@next sveltekit-auth
<span class="hljs-comment"># In this example I selected sceleton project and said NO to TypeScript, YES to ESLint and YES to Prettier</span>
<span class="hljs-built_in">cd</span> sveltekit-auth
npm install

<span class="hljs-comment"># Open the website in the browser:</span>
npm run dev -- --open
</code></pre>
<p>Because we're going to store the authentication details (e.g. session token) in the cookies, let's also add the <code>cookie</code> package:</p>
<pre><code class="lang-bash">npm i cookie
</code></pre>
<h2 id="heading-the-authentication-hook">The Authentication hook</h2>
<p>Most likely you want to validate the users session on every request to the website. So instead of implementing this logic on every page, we'll create a hook that'll be executed on every page request. Inside of this hook, we can add data to the request that we can access later in our code (e.g. the pages themselves).</p>
<p>To create a new hook, let's create a new file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// /src/hooks.js</span>

<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cookie <span class="hljs-keyword">from</span> <span class="hljs-string">"cookie"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSession</span>(<span class="hljs-params">{ headers }</span>) </span>{
    <span class="hljs-keyword">const</span> cookies = cookie.parse(headers.cookie || <span class="hljs-string">""</span>)

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">if</span>(cookies.sessionid) { <span class="hljs-comment">// Check for a sessionID</span>
            <span class="hljs-keyword">const</span> sessionid = cookies.sessionid <span class="hljs-comment">// Get the sessionID</span>
            <span class="hljs-comment">// ! This is for this example ONLY. In production, here would be something</span>
            <span class="hljs-comment">// like a database query to validate the session</span>
            <span class="hljs-keyword">if</span>(sessionid === <span class="hljs-string">"session1"</span>) {
                <span class="hljs-keyword">return</span> {
                    <span class="hljs-attr">authenticated</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">isAdmin</span>: <span class="hljs-literal">true</span>,
                }
            }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(sessionid === <span class="hljs-string">"session2"</span>) {
                <span class="hljs-keyword">return</span> {
                    <span class="hljs-attr">authenticated</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">isAdmin</span>: <span class="hljs-literal">false</span>,
                }
            }
        }
    }<span class="hljs-keyword">catch</span>(error) {
        <span class="hljs-built_in">console</span>.log(error)
    }

    <span class="hljs-comment">// By default, the user is not authenticated</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">authenticated</span>: <span class="hljs-literal">false</span>,
    }
}
</code></pre>
<p>What this code does is pretty simple - we're exporting a function that takes the request headers as an argument, because that's where the cookies are stored in. Then we parse the cookie string from the header to a javascript object and check for the cookie we need (<code>sessionid</code>). If this cookie is present, we check for our two test-sessions and return <code>authenticated: true</code> together with the <code>isAdmin</code> boolean. That's just for testing - you'll implement something like a databse query to validate the session.</p>
<p>As you see, we're returning always the <code>authentication</code> boolean, so we can use this later in our code to determinate if the user is logged in or not. You can here also store something like the username or email if needed.</p>
<h2 id="heading-load-method">load() method</h2>
<p>Now we need a way to get the authentication state. In our pages, we can do that with the <code>load</code> function. That's the function executed on the server when we request the page. Export it in the <code>module</code> context:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- /src/routes/index.svelte --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript">

    <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">load</span>(<span class="hljs-params">{ session }</span>) </span>{
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">props</span>: {
                <span class="hljs-attr">loggedIn</span>: session.authenticated,
                <span class="hljs-attr">admin</span>: session?.isAdmin || <span class="hljs-literal">false</span>,
            }
        }
    }

</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">

    <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> loggedIn, admin

</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    You {loggedIn ? "are" : "are not"} logged in and your rank is {admin ? "admin" : "guest"}.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>This example simply passes the loggedIn and admin status to the page. But that's the point where you can add your own logic and use the authentication state for database queries or redirect the user if it's a private page. You can redirect the user like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> {
    <span class="hljs-attr">status</span>: <span class="hljs-number">403</span>,
    <span class="hljs-attr">redirect</span>: <span class="hljs-string">"/login"</span>
}
</code></pre>
<p>Open the page in your browser and you should see the following text:</p>
<pre><code class="lang-plain_text">You are not logged in and your rank is guest.
</code></pre>
<p>Pull up the devtools and set the cookie <code>sessionid</code> to <code>session1</code>, now you should see this text:</p>
<pre><code class="lang-plain_text">You are logged in and your rank is admin.
</code></pre>
<h2 id="heading-the-login-and-logout-components">The login and logout components</h2>
<p>As I said before, I won't implement a real database or user system in this example. In your login component, the only thing you need to do is to save the sessionID or whatever you need to authenticate the user in the cookies (same name you used in <code>/src/hooks.js</code>). And after the user logged out, remove the cookie.</p>
<p>That's how authentication in SvelteKit generally works. I'll create a more detailed guide with a real database and login system in the future, so make sure to follow my blog or join my newsletter to get notified when the article comes out.</p>
<p>Thank you for reading and have a nice day 🤗🎉</p>
<div class="hn-embed-widget" id="bmac"></div>]]></content:encoded></item><item><title><![CDATA[Get started with TailwindCSS]]></title><description><![CDATA[First things first, this post is about how to get started, not should you get started with TailwindCSS. I've read much articles about why TailwindCSS is good or bad, I personally really like it and use it in much of my projects. If you do so as well ...]]></description><link>https://linu.us/get-started-with-tailwindcss</link><guid isPermaLink="true">https://linu.us/get-started-with-tailwindcss</guid><category><![CDATA[#howtos]]></category><category><![CDATA[Tailwind CSS]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sat, 20 Nov 2021 20:51:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885379748/UgsAt-JVM.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>First things first, this post is about <strong>how</strong> to get started, not <strong>should</strong> you get started with TailwindCSS. I've read much articles about why TailwindCSS is good or bad, I personally really like it and use it in much of my projects. If you do so as well or want to try it out, this article is for you.</p>
<h2 id="heading-how-tailwindcss-works">How TailwindCSS works</h2>
<p>As every technology, TailwindCSS changed over the last months and there are many ways to get things done. As of now, I'm aware of 3 ways to use TailwindCSS in your project:</p>
<h3 id="heading-postcss">PostCSS</h3>
<p>You can use TailwindCSS as a plugin for  <a target="_blank" href="https://postcss.org/">PostCSS</a>. What's really awesome about using this method is that you have the full power of PostCSS available. As an example, you can add <a target="_blank" href="https://www.npmjs.com/package/autoprefixer">autoprefixer</a> to automatically add vendor prefixes or use a preprocessor like <a target="_blank" href="https://sass-lang.com/">Sass</a>.</p>
<h3 id="heading-command-line-cli">Command line (CLI)</h3>
<p>If you don't want to setup PostCSS and do all of that configuration stuff, you can use the CLI to get started quickly and without much configuration required.</p>
<h3 id="heading-cdn">CDN</h3>
<p>Actually, this isn't really a method like the other ones, because it's <strong>not recommended for the production build</strong>. Click here for  <a target="_blank" href="https://tailwindcss.com/docs/installation#using-tailwind-via-cdn">more information about the CDN build</a>. <em>(Same thing for the <a target="_blank" href="https://github.com/tailwindlabs/tailwindcss/releases/tag/v3.0.0-alpha.1#just-in-time-cd">CDN JIT build</a>  coming with TailwindCSS 3.0)</em></p>
<h2 id="heading-just-in-time-jit">Just-in-time (JIT)</h2>
<p>Recently, TailwindLabs released a preview version of the  <a target="_blank" href="https://tailwindcss.com/docs/just-in-time-mode#enabling-jit-mode">Just-in-time compiler</a>  as the "<a target="_blank" href="https://www.youtube.com/watch?v=3O_3X7InOw8">Next generation of TailwindCSS</a>".</p>
<p>Basically, the core concept is that TailwindCSS scanns your code for TailwindCSS-classes on every change and only generates the classes you need. This also introduced new possibilities and features like <a target="_blank" href="https://tailwindcss.com/docs/just-in-time-mode#arbitrary-value-support">arbitrary values</a>, all features available by default and much more. <strong>We'll use the JIT compiler in this article.</strong></p>
<p><em>(The JIT compiler becomes the default compiler in TailwindCSS 3.0)</em></p>
<h2 id="heading-prepare-the-project">Prepare the project</h2>
<p>Now it's time to get our hands on and start our code editor. Open a new folder and create a <code>index.html</code> file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"build.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Note the linked CSS file <code>build.css</code>. We'll use this filename later in the guide as output filename for PostCSS / TailwindCSS.</p>
<p>We also need a CSS file as entry point. In this example I'll name this file <code>style.css</code>. In this file, we import the TailwindCSS classes / layers:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<h2 id="heading-create-the-config-file">Create the config file</h2>
<p>To create the config file, we can simply run this command:</p>
<pre><code class="lang-bash">// If you want to use TailwindCSS with PostCSS (also generates the PostCSS config):
npx tailwindcss init -p

// If you want to use the CLI:
npx tailwindcss init
</code></pre>
<p>Because we want to use the JIT compiler, we have to do some changes here. Set the <code>mode</code> option in the <code>tailwind.config.js</code> file to <code>jit</code> to enable it. Because the JIT compiler looks at our code after changes, we need to tell the engine on which files to look at. <strong>This step is required for the JIT compiler to work properly.</strong></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">mode</span>: <span class="hljs-string">"jit"</span>,
  <span class="hljs-attr">purge</span>: [
    <span class="hljs-string">"./index.html"</span>, <span class="hljs-comment">// &lt;- The HTML file we created</span>
    <span class="hljs-string">"./src/**/*.html"</span>, <span class="hljs-comment">// &lt;- You can also add a whole directory</span>
    <span class="hljs-string">"./src/**/*.js"</span>, <span class="hljs-comment">// &lt;- And other file types</span>
  ],
  <span class="hljs-attr">darkMode</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// or 'media' or 'class'</span>
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>In this example, we only need <code>./index.html</code> inside the <code>purge</code> array, because that's the only file we have.</p>
<p><strong>Note:</strong> With TailwindCSS 3.0, <code>purge</code> may be renamed to <code>content</code>. So maybe this change is already live when you're reading this article.</p>
<p>Also, we can get rid of the <code>variants</code> configuration stuff in the config because the JIT compiler enables all variants by default.</p>
<h2 id="heading-use-the-cli">Use the CLI</h2>
<p>Let's start with the simpler method - the CLI. It's basically just one command to build the project:</p>
<pre><code class="lang-bash">npx tailwindcss -i ./style.css -o ./build.css
</code></pre>
<p>The <code>-i</code> argument is the input CSS file, in our case that's the <code>style.css</code> file we've created earlier. The <code>-o</code> parameter is the output file.</p>
<p>This command runs TailwindCSS once. While development, you'll add the <code>-w</code> argument to start TailwindCSS in watch mode:</p>
<pre><code class="lang-bash">npx tailwindcss -i ./style.css -o ./build.css -w
</code></pre>
<p>That's all you need to get started with the CLI.</p>
<h2 id="heading-use-postcss">Use PostCSS</h2>
<p>If you want to use TailwindCSS with PostCSS, first initialize npm:</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>Now, we can install all the dependencies we need:</p>
<pre><code class="lang-`bash">npm install -D tailwindcss postcss postcss-cli autoprefixer
</code></pre>
<p>After the dependencies are installed, we can setup two scripts in our <code>package.json</code>, one to build the project and one for development:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"example-postcss"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"postcss.config.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"postcss ./style.css -o ./build.css"</span>,
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"postcss ./style.css -o ./build.css -w"</span>
  },
  <span class="hljs-attr">"keywords"</span>: [],
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"autoprefixer"</span>: <span class="hljs-string">"^10.4.0"</span>,
    <span class="hljs-attr">"postcss"</span>: <span class="hljs-string">"^8.3.11"</span>,
    <span class="hljs-attr">"postcss-cli"</span>: <span class="hljs-string">"^9.0.2"</span>,
    <span class="hljs-attr">"tailwindcss"</span>: <span class="hljs-string">"^2.2.19"</span>
  }
}
</code></pre>
<p>The <code>postcss</code> commands are similar to the TailwindCSS CLI, the first argument is the input file, the second (<code>-o</code>) is the output file and for the development command we add <code>-w</code> to enable the watch-mode.</p>
<p>We can now run PostCSS using the commands we've just created:</p>
<pre><code class="lang-bash">// Build
npm run build
// Development
npm run dev
</code></pre>
<p><strong>That's it! 🎉</strong></p>
<p>TailwindCSS is now installed and you can start using it. I won't teach you how to use it, because I think it's mostly self-explainatory and the <a target="_blank" href="https://tailwindcss.com/docs">official TailwindCSS docs</a> are already very helpful.</p>
<p>I hope you liked this guide and you're now ready to start with TailwindCSS.</p>
<p>For more content on TailwindCSS, I can highly recommend their <a target="_blank" href="https://www.youtube.com/channel/UCOe-8z68tgw9ioqVvYM4ddQ">official YouTube-Channel</a>. They create awesome screencasts about TailwindCSS and all it's features and possibilities.</p>
<p>I'll leave with this video recommendation, it's about how to customize TailwindCSS to your needs, add your brand colors and more:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=cZc4Jn5nK3k">https://www.youtube.com/watch?v=cZc4Jn5nK3k</a></div>
<p>Thank you for reading and have a nice day 🤗👋</p>
<div class="hn-embed-widget" id="bmac"></div>]]></content:encoded></item><item><title><![CDATA[How to nest groups in TailwindCSS]]></title><description><![CDATA[Update:  This article is now outdated as nested groups have become a core feature of Tailwind CSS 3.2.👉 Official docs: https://tailwindcss.com/docs/hover-focus-and-other-states#differentiating-nested-groups👉 Playground example: https://play.tailwin...]]></description><link>https://linu.us/how-to-nest-groups-in-tailwindcss</link><guid isPermaLink="true">https://linu.us/how-to-nest-groups-in-tailwindcss</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Tailwind CSS Tutorial]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[plugin]]></category><dc:creator><![CDATA[Linus Benkner]]></dc:creator><pubDate>Sat, 20 Nov 2021 16:12:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649885336808/pxwsZ3nNX.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong><mark>Update:</mark></strong> <mark> This article is now outdated</mark> as nested groups have become a core feature of Tailwind CSS 3.2.<br />👉 Official docs: <a target="_blank" href="https://tailwindcss.com/docs/hover-focus-and-other-states#differentiating-nested-groups">https://tailwindcss.com/docs/hover-focus-and-other-states#differentiating-nested-groups</a><br />👉 Playground example: <a target="_blank" href="https://play.tailwindcss.com/IqzVJD0rLe">https://play.tailwindcss.com/IqzVJD0rLe</a></p>
<hr />
<p>If you're using TailwindCSS in your projects, I'm pretty sure you're familiar with the group utilities. If you're not, here is a quick overview:</p>
<h2 id="heading-groups-in-tailwindcss">Groups in TailwindCSS</h2>
<p>When working with TailwindCSS, you use a bunch of classnames to style your HTML elements. But imagine you have a card element and want to change elements inside when hovering over the card. You can't just use the <code>hover</code> utility, because this only applies when you hover over the element you applied the class to.</p>
<p>That's where <a target="_blank" href="https://tailwindcss.com/docs/hover-focus-and-other-states#group-hover">groups</a> come into play. You add the <code>group</code> class to the parent element and use <code>group-hover</code> (or <code>group-focus</code>, <code>group-active</code>, ...) on the child elements. The <code>group-hover</code> variant applies, when you hover over the parent element with the <code>group</code> class.</p>
<h2 id="heading-how-groups-work-and-why-we-cant-nest-them">How groups work and why we can't nest them</h2>
<p>To understand why we can't nest groups in TailwindCSS, we first need an overview on how the group utility actually works. For example, the class <code>group-hover:underline</code> generates this CSS code:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.group</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-class">.group-hover</span>\<span class="hljs-selector-pseudo">:underline</span> {
     <span class="hljs-attribute">text-decoration</span>: underline;
}
</code></pre>
<p>I guess it's pretty clear why we can't nest groups. The text-decoration property applies to our element, when it's inside any group with the hover state.</p>
<p>Here is an example with two nested boxes with the group utility:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 group bg-yellow-200"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"group-hover:underline"</span>&gt;</span>Hello world<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 group bg-yellow-400"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"group-hover:underline"</span>&gt;</span>Hello world<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>You can test it out <a target="_blank" href="https://play.tailwindcss.com/YSYnEj7mlB">here on the TailwindCSS playground</a>. But notice how both texts are underlined when hovering over a box.</p>
<h2 id="heading-how-to-enable-nesting">How to enable nesting</h2>
<p>There are a few packages out there claiming to solve this issue, but all of the packages I tested were outdated or incompatible with the new JIT compiler. That's why I started building my own package to solve this problem. You can install it via NPM and add it to your <code>tailwind.config.js</code>:</p>
<pre><code class="lang-bash">npm i tailwindcss-scoped-groups
</code></pre>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-attr">plugins</span>: [
    <span class="hljs-built_in">require</span>(<span class="hljs-string">"tailwindcss-scoped-groups"</span>),
  ],
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>This plugin adds a new utility called <code>group-scoped</code>. You can use it just as you'd use the default group utility, but use <code>group-scoped</code> instead of <code>group</code> (e.g. <code>group-scoped-hover:underline</code>).</p>
<p>I've created an example on the <a target="_blank" href="https://play.tailwindcss.com/8ednkXaT9F">TailwindCSS playground, check it our here</a> .</p>
<p>That's how our example above would look like with this plugin:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 group-scoped bg-yellow-200"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"group-scoped-hover:underline"</span>&gt;</span>Hello world<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 group-scoped bg-yellow-400"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"group-scoped-hover:underline"</span>&gt;</span>Hello world<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>And that's it - a simple but effective way to enable nested groups in TailwindCSS.</p>
<p><strong>Thank you very much for reading this article 🤗 and have a nice day 🎉</strong></p>
<div class="hn-embed-widget" id="bmac"></div>]]></content:encoded></item></channel></rss>