<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: visuellverstehen</title>
    <description>The latest articles on DEV Community by visuellverstehen (@visuellverstehen).</description>
    <link>https://dev.to/visuellverstehen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3606%2Fa0fbc173-24e6-43d6-9dc2-7c9646513d6c.png</url>
      <title>DEV Community: visuellverstehen</title>
      <link>https://dev.to/visuellverstehen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/visuellverstehen"/>
    <language>en</language>
    <item>
      <title>Access Current Entry in Custom Validation Rule</title>
      <dc:creator>Julia Lange</dc:creator>
      <pubDate>Fri, 22 Dec 2023 08:57:01 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/access-current-entry-in-custom-validation-rule-5ao3</link>
      <guid>https://dev.to/visuellverstehen/access-current-entry-in-custom-validation-rule-5ao3</guid>
      <description>&lt;p&gt;Sometimes, when writing a custom validation rule for a Statamic project, I find myself in a position where I need information about the current entry I am editing. &lt;/p&gt;

&lt;p&gt;But because validation rules don’t provide this kind of information, I've tried some hacky stuff like reading the current entry id from the request URL. However that doesn't work if you have more than two stacks open at once. &lt;/p&gt;

&lt;p&gt;Luckily I discovered that Statamic did a nifty thing with their own &lt;a href="https://statamic.dev/validation#available-rules"&gt;validation rules&lt;/a&gt;. Just like &lt;code&gt;unique_entry_value&lt;/code&gt;, &lt;code&gt;unique_term_value&lt;/code&gt; &amp;amp; &lt;code&gt;unique_user_value&lt;/code&gt; you can pass the current id, collection and the site like this to your validation rule:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;resources/blueprints/collections/pages/default.yaml&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;custom_validation_rule:{id},{collection},{site}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those values can then be found in the parameters array inside the validate method. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;App/Validation/CustomValidationRule.php&lt;/code&gt;&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomValidationRule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nv"&gt;$collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nv"&gt;$site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// Do stuff with it.&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all there is to it. Simple, but powerful. Happy coding, everyone!&lt;/p&gt;

</description>
      <category>statamic</category>
      <category>validation</category>
      <category>fieldtypes</category>
    </item>
    <item>
      <title>Create an entry approval workflow with Statamic Revisions</title>
      <dc:creator>Lakkes</dc:creator>
      <pubDate>Thu, 14 Sep 2023 09:14:38 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/create-an-entry-approval-workflow-with-statamic-revisions-4gnc</link>
      <guid>https://dev.to/visuellverstehen/create-an-entry-approval-workflow-with-statamic-revisions-4gnc</guid>
      <description>&lt;p&gt;When managing entries in your Statamic application that gather external data, such as from APIs, you may find it necessary to implement a data control step before publication. Statamic offers a useful tool for this purpose known as the revisions feature, specifically focusing on &lt;strong&gt;working copies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the following sections, I'll guide you through the process of creating an Approval Mechanism for Statamic.&lt;/p&gt;

&lt;p&gt;This article is structured as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who is this article for?&lt;/li&gt;
&lt;li&gt;What we are about to do&lt;/li&gt;
&lt;li&gt;Let's bring in the data!&lt;/li&gt;
&lt;li&gt;Let authors know that there's something to review&lt;/li&gt;
&lt;li&gt;Create a feedback form&lt;/li&gt;
&lt;li&gt;Tracking feedback and dates&lt;/li&gt;
&lt;li&gt;Handling publication&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who is this article for?
&lt;/h2&gt;

&lt;p&gt;This article is primarily aimed at Statamic developers who are already acquainted with Statamic's core concepts and have a basic understanding of PHP and Laravel. To fully comprehend and implement the Approval Mechanism discussed here, readers should be familiar with key Statamic elements like entries and blueprints.&lt;/p&gt;

&lt;p&gt;Additionally, a basic grasp of Vue.js for handling user interface components and API integration knowledge will prove beneficial in following concepts presented in this article.&lt;/p&gt;

&lt;p&gt;I would recommended that you also have a basic understanding of &lt;a href="https://statamic.dev/extending" rel="noopener noreferrer"&gt;extending Statamic&lt;/a&gt;, but if you're new to this, it's a good idea to explore those concepts beforehand for a smoother learning experience.&lt;/p&gt;

&lt;p&gt;You need &lt;strong&gt;Statamic Pro&lt;/strong&gt; to enable the revisions feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are about to do
&lt;/h2&gt;

&lt;p&gt;In this article, our primary objective is to create an Approval Mechanism within Statamic, emphasizing the process of managing entries and enabling a structured review and feedback workflow. While we briefly touch on data import as context, our main focus lies in developing this mechanism. We'll explore how to efficiently flag entries as 'Needs Review,' facilitate feedback from CMS authors through a built-in feedback form, and dynamically update entry statuses for better organization.&lt;/p&gt;

&lt;p&gt;Additionally, we'll demonstrate how to log feedback and maintain feedback history as part of this comprehensive solution. For illustration, we'll use a 'persons' collection where each person is associated with a unique 'external_service_id,' linking them to external data sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's a working copy?
&lt;/h2&gt;

&lt;p&gt;Working copies serve as a &lt;strong&gt;temporary version&lt;/strong&gt; of an entry, generated when you edit and save an existing entry. You then have the option to create a revision or proceed with publication. Should you opt for a revision or publication, the working copy will be discarded. Conversely, choosing to save your changes ensures that the working copy is promptly updated.&lt;/p&gt;

&lt;p&gt;By default working copies are saved inside the revisions folder. The path can be set in the &lt;code&gt;config/statamic/revisions.php&lt;/code&gt; file. By default it's set to &lt;code&gt;storage_path('statamic/revisions')&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's bring in the data!
&lt;/h2&gt;

&lt;p&gt;We assume we already have some existing persons in our collection. Inside our import method we want to update or create a working copy of each entry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$personEntries&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$personEntry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$personEntry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Update working copy&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create working copy&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's implement these methods. We are assuming that the &lt;strong&gt;new data&lt;/strong&gt; comes in an array that represents the data structure of our person blueprint. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$newData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Smeagol'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'diet'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'juicy sweet fish'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'job_title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Tourist Guide'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating&lt;/strong&gt; the working copy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Entry&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Set the new data&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a working copy from the entry&lt;/span&gt;
    &lt;span class="nv"&gt;$workingCopy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;makeWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Save the working copy&lt;/span&gt;
    &lt;span class="nv"&gt;$workingCopy&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Updating&lt;/strong&gt; the working copy is a little more complicated since the data can't be set directly on the working copy. We have to get the data from the working copy, update it and then save it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Entry&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$newData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get current data from the data attribute and merge it with the new data.&lt;/span&gt;
    &lt;span class="nv"&gt;$currentData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;workingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$updatedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$currentData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$newPureData&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;$entry&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;workingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$updatedData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, at the moment, we always create or update a working copy and overwrite a possible existing one. In this case, that's okay, but you may want to check if there is already a working copy, and if so, create a new revision instead of overwriting the working copy. But that results in potentially a lot of revisions. So you have to decide what's best for you. I will only cover the working copy case here.&lt;/p&gt;

&lt;p&gt;Additionally, you should have a field in your entries blueprint where you save the last imported date to check if the incoming data is newer than the last import. If not, you can skip the entry. That, of course, presupposes that your external data also provides some kind of 'updated_at' field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let authors know that there's something to review
&lt;/h2&gt;

&lt;p&gt;What we did before is basically everything you need to have a simple approval mechanism where new data gets saved inside a working copy to be reviewed. But we want to give the CMS authors a hint that there is something to review. We will do this by adding a status field.&lt;/p&gt;

&lt;p&gt;What we need is a custom field type that we can use in our blueprint. I won't go into detail here about how to create a custom field type. You can read about it in the &lt;a href="https://statamic.dev/extending/fieldtypes" rel="noopener noreferrer"&gt;Statamic documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will create a custom field type called 'approval_status' that will have three possible values: 'needs_review,' 'approved,' and 'feedback_sent.'&lt;/p&gt;

&lt;p&gt;We will add some methods to our ApprovalStatus.php fieldtype class to check the status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;preProcessIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getStatus&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ApprovalStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it can be better to outsource the logic to a separate class. We will create an ApprovalStatus class that will have a static method to get the status. This method will use a variable &lt;code&gt;$feedbackDate&lt;/code&gt;. This variable will be set when the CMS author gives feedback and deleted when the working copy is published. We will cover this later.&lt;/p&gt;

&lt;p&gt;Here's a little diagram that visually explains the logic behind the status check:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nxdpy58dxq30m44tqp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nxdpy58dxq30m44tqp1.png" alt="The logic behind the approval status"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;… and the corresponding code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Entry&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$workingCopyData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;workingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$feedbackDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$workingCopyData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'feedback_date'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// The person has no working copy and is published, so there's noting to be reviewed.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;published&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'approved'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// The person has a working copy and has no feedback date, so a review was sent.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$feedbackDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'feedback_sent'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// The person has a working copy and has no feedback date, so it needs a review.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$feedbackDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'needs_review'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Vue components to our ApprovalStatus field type class are pretty simple. They get the status with the help of the &lt;code&gt;preload()&lt;/code&gt; and the &lt;code&gt;preProcessIndex()&lt;/code&gt; methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ApprovalStatus.vue&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ApprovalStatusIndex.vue&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also add your classes and pass a color to your status array to style the status in the control panel. You can find more information about index field types here: &lt;a href="https://statamic.dev/extending/fieldtypes#index-fieldtypes" rel="noopener noreferrer"&gt;Statamic Index Fieldtypes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Giving Feedback
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5srd95lx1jyrqrp9731.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5srd95lx1jyrqrp9731.png" alt="Feedback Workflow Step 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1svk7xgo2zc7hokwxkzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1svk7xgo2zc7hokwxkzd.png" alt="Feedback Workflow Step 2 and 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkoe76v667jodlxrodzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkoe76v667jodlxrodzo.png" alt="Feedback Wokflow Step 4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a feedback form
&lt;/h3&gt;

&lt;p&gt;The form is also a custom fieldtype. It will have a textarea and a button to submit the feedback. We assume that the person entry has an email stored. We can write our feedback into the text field and with the click on the button, we will make a post request to a custom route that takes care of error handling and sending the mail. After sending the mail, we want to disable the form and the button to prevent multiple feedbacks.&lt;/p&gt;

&lt;p&gt;This is how the Vue component can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/textarea&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Send&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;mail&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mail&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mailSent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendEmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this.mailSent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Mail&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;way&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Send&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mixins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Fieldtype&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;storeName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;mailSent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;review_mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin/person/review/send&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;review_mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`E-Mail sent to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mailSent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;review_mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mailSent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tracking feedback and dates
&lt;/h3&gt;

&lt;p&gt;To allow a better overview of previous sent feedback, you can log your feedback in a replicator field below the feedback form. Simply add some code to the method that handles your post request from the form component above. Let's assume this method is inside the same class as the create and update working copy methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;saveFeedback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$entryId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$feedbackMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entryId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$currentFeedbackLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;workingCopy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'feedback_logs'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nv"&gt;$newLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'log'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'enabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;RowId&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'mail'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$feedbackLog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'time'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'d.m.Y H:i:s'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;$updatedLog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$newLog&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$currentFeedbackLog&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateWorkingCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'feedback_logs'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$updatedLog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'feedback_date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Y-m-d\TH:i:s.uP'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling publication
&lt;/h2&gt;

&lt;p&gt;To finish up the approval mechanism, we just have to delete the feedback date automatically when the entry gets published. We can achieve this by &lt;a href="https://statamic.dev/extending/events#overview" rel="noopener noreferrer"&gt;listening to the &lt;code&gt;EntrySaved&lt;/code&gt;&lt;/a&gt; vent. We will create a listener class that will check if the entry is from the persons collection and has set a feedback date and if so, delete the feedback date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$collectionHandle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$collectionHandle&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'persons'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;feedback_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'feedback_date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;saveQuietly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how the Feedback UI could look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hrd3cvcm79d4nhluex7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hrd3cvcm79d4nhluex7.png" alt="The entry feedback form, status and log"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwftrfct9owxt7l4e9rrn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwftrfct9owxt7l4e9rrn.png" alt="The index view with status"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Great! 🎉 We now have a simple approval mechanism for our Statamic application. Of course, you can extend this mechanism to your needs. For example, you can add a field to your entries blueprint where you can set the email address of the person that should review the entry or for the person that should review it. It's also very handy to add a filter to your index view to filter for entries that need a review.&lt;/p&gt;

&lt;p&gt;When handling multisites, you should think about if you want to have a separate approval mechanism for each site or if you want to have a global one. In my case, I disabled the review form for any other language than the default one, and the authors are always sending reviews for all localizations in one mail. When creating and updating the working copy, you have to update each localization separately. Take a look at the &lt;code&gt;$entry-&amp;gt;descendants()&lt;/code&gt; method to get all localizations of an entry.&lt;/p&gt;

&lt;p&gt;I hope this article was helpful for you. If you have any questions or suggestions, please let me know in the comments. 👋&lt;/p&gt;

</description>
      <category>statamic</category>
      <category>webdev</category>
      <category>laravel</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Effortless Language Redirection - TYPO3 Extension</title>
      <dc:creator>Yamen Hadla</dc:creator>
      <pubDate>Thu, 23 Mar 2023 08:27:09 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/effortless-language-redirection-typo3-extension-2514</link>
      <guid>https://dev.to/visuellverstehen/effortless-language-redirection-typo3-extension-2514</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello fellow TYPO3 enthusiasts! I had the pleasure of cooperating with my colleague &lt;a class="mentioned-user" href="https://dev.to/malteriechmann"&gt;@malteriechmann&lt;/a&gt; to create a TYPO3 extension called &lt;code&gt;t3languageredirection&lt;/code&gt;, which provides an effortless and dependable way to redirect users to their preferred language. In this blog post, we will take a look at this simple project, and reveal how it can add user-friendliness to your multilingual website.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does &lt;code&gt;t3languageredirection&lt;/code&gt; actually do?
&lt;/h2&gt;

&lt;p&gt;This &lt;strong&gt;open-source&lt;/strong&gt; extension, will configure a new frontend middleware that automatically redirects users to the appropriate language version of your website based on their browser's Accept-Language header.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;LanguageRedirectionMiddleware&lt;/code&gt; is implemented using the &lt;a href="https://www.php-fig.org/psr/psr-15/"&gt;PSR-15&lt;/a&gt; middleware interface, making it easy to integrate into your existing TYPO3 project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;t3languageredirection&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Unlike other TYPO3 redirection extensions, t3languageredirection does not require any additional configuration and is not dependent on the user's IP address. Additionally, it is designed to be independent of the underlying web servers, providing a flexible and adaptable solution.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;t3languageredirection&lt;/code&gt; will improve the user experience of a website and make it more accessible to a wider audience. Whether your website is a small blog or a large e-commerce site, this extension is a nice-to-have tool for anyone looking to create a multilingual TYPO3 website.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use &lt;code&gt;t3languageredirection&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;To get started, simply install the extension using Composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require visuellverstehen/t3languageredirection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation the middleware will automatically handle language redirection. This means that visitors to your site will be redirected to the language version that best suits their needs without any additional effort on your part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;t3languageredirection&lt;/code&gt; extension simplifies the language redirection of your multilingual website with easy installation, automatic redirection, and seamless compatibility with any hosting environment. The extension is still in the testing phase, and we are constantly working to refine and improve its functionality.&lt;/p&gt;

&lt;p&gt;Feel free to utilise it or to give us feedbacks, and if you could star the repository, we would appreciate it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/visuellverstehen/t3languageredirection"&gt;https://github.com/visuellverstehen/t3languageredirection&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typo3</category>
      <category>php</category>
      <category>github</category>
    </item>
    <item>
      <title>Accessible website: 7 steps to better accessibility and SEO performance</title>
      <dc:creator>Dorien Grönwald</dc:creator>
      <pubDate>Thu, 12 Jan 2023 13:20:17 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/accessible-website-7-steps-to-better-accessibility-and-seo-performance-4afm</link>
      <guid>https://dev.to/visuellverstehen/accessible-website-7-steps-to-better-accessibility-and-seo-performance-4afm</guid>
      <description>&lt;p&gt;Web accessibility enables people with physical and/or mental disabilities to use the Internet with as few barriers as possible. But to make a website accessible, many different factors must be taken into account. &lt;/p&gt;

&lt;p&gt;However, there are a few small adjusting screws that not only make a website more inclusive, but also serve search engine optimization (SEO).&lt;/p&gt;




&lt;h2&gt;
  
  
  7 steps you shall take
&lt;/h2&gt;

&lt;p&gt;The following points have a positive effect on digital accessibility and at the same time can lead to a better ranking of the website, which in turn ideally expands the target group and increases the site's traffic. A real win-win situation.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtx6btuf8em7a8034lm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtx6btuf8em7a8034lm9.png" alt="A well-structured website helps assistive technologies to better allocate content." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Clean structure of the website
&lt;/h2&gt;

&lt;p&gt;A clean HTML structure and proper semantics are the foundation for a well-performing and inclusive website. This is because if a site is structured in a sensible way, search engines can easily understand what kind of information and content is present and how it needs to be processed.&lt;/p&gt;

&lt;p&gt;However, this doesn't just apply to search engine crawlers - programs that crawl and index websites. Assistive technologies such as screen readers, which read the content of websites aloud to users with visual impairments, also depend on websites being well structured. For example, the better the content is presented, the easier it is for the screen reader to distinguish between a list and normal continuous text.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1qmmmguavqp37hqqnfyl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1qmmmguavqp37hqqnfyl.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Clearly formulated headings in the correct order
&lt;/h2&gt;

&lt;p&gt;Structure and semantics should also be taken into account for headings. Because mistakes often happen here - although from a technical point of view only two rules need to be followed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Only one H1 must and may be defined on a page.&lt;/li&gt;
&lt;li&gt;All headings must be placed in the correct order. For example, an H2 may not be followed by an H4 without an H3 also having been defined first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The search engine tries to create a factual context from the headings and the subordinate content to which the page can be ranked - i.e. placed in the search results of a search engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headings as a navigation aid&lt;/strong&gt;&lt;br&gt;
The meaning of headings for assistive technologies is somewhat different. For example, users can navigate back and forth between headings and thus jump directly to specific content on a website. Here, too, headings that are valuable in terms of content and correctly labeled in terms of hierarchy are desirable.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqv1mfuwbskja43xvtlcp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqv1mfuwbskja43xvtlcp.png" alt="The presentation of links and buttons is also an important factor for both search engine optimization and web accessibility." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Correct labeling of links and buttons
&lt;/h2&gt;

&lt;p&gt;Both from an SEO point of view and with regard to web accessibility, a lot can be optimized using buttons and links. The following list summarizes the most important points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Correct distinction between a button (&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;) and a link (&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Link and button texts with valuable content&lt;/li&gt;
&lt;li&gt;Links and buttons should be clickable and also imply this visually&lt;/li&gt;
&lt;li&gt;Visual highlighting when navigating through the page with "Tab"&lt;/li&gt;
&lt;li&gt;Links and buttons should have an aria-label or a title&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq037plsh7mpz4zmg4nbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq037plsh7mpz4zmg4nbo.png" alt="To ensure fast loading speed of the website, various factors can be optimized." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Loading speed
&lt;/h2&gt;

&lt;p&gt;As soon as a website takes too long to load, visitors bounce and this is reflected negatively in the SEO ranking. The most important factors for a fast loading speed are, among others, good caching and the correct dimensioning of images in modern formats. Furthermore, no unnecessary scripts or CSS files should be loaded that are not needed on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practice&lt;/strong&gt;&lt;br&gt;
Furthermore, it is recommended that the server should use the http/2 protocol. This can be easily tested at: &lt;a href="https://http2.pro/" rel="noopener noreferrer"&gt;https://http2.pro/&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57587voxv9kzwljn3bo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57587voxv9kzwljn3bo2.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Images and SVGs
&lt;/h2&gt;

&lt;p&gt;Optimizing images is not only an important issue regarding loading speed and SEO, but also for the accessibility of a website. For example, images and SVGs should be optimized for people with impaired vision so as not to present a barrier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When words say what pictures cannot&lt;/strong&gt;&lt;br&gt;
Probably the most important tools for this are the alt attribute for images and the title tag for SVGs. A short description is stored there. This not only helps users with screen readers to understand what is being displayed. Search engines can also understand the context more easily and better classify the displayed content. &lt;/p&gt;

&lt;p&gt;Purely decorative images and SVGs can and should be hidden from the screen reader with aria attributes, as they do not provide any added value.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqj3cq6fmp2lgrubjaqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqj3cq6fmp2lgrubjaqw.png" alt="The sitemap is important for the indexing and overview of a website." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Sitemap
&lt;/h2&gt;

&lt;p&gt;This point is also important for SEO as well as for accessibility. The sitemap can help search engines to index all existing pages. Only then can a page be found in Google search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple overview&lt;/strong&gt;&lt;br&gt;
In addition to indexing, the sitemap helps users with disabilities get an overview of all pages and navigate back and forth between them. This way, no one has to fight their way through complex navigation structures with the keyboard.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeqi7ao5kogw0nia7k9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeqi7ao5kogw0nia7k9t.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Page title
&lt;/h2&gt;

&lt;p&gt;The page title is set by the meta title and defines which title will be displayed in Google search as well as at the top of the tab. &lt;/p&gt;

&lt;p&gt;As an official ranking factor, a meaningful title is not only essential for search engine optimization, but also for the orientation of screen reader users. This allows them to quickly distinguish between open tabs and select the page they are looking for.&lt;/p&gt;




&lt;h2&gt;
  
  
  More accessibility and SEO performance for your website
&lt;/h2&gt;

&lt;p&gt;Would you like to know where your website still has potential for improvement in the area of SEO or do you have questions on the subject of web accessibility?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.visuellverstehen.de/kontakt" rel="noopener noreferrer"&gt;Contact us&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>programming</category>
      <category>solidity</category>
    </item>
    <item>
      <title>New git guidelines: We have switched to Conventional Commits</title>
      <dc:creator>Malte Riechmann</dc:creator>
      <pubDate>Sat, 22 Oct 2022 08:28:39 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/new-git-guidelines-we-have-switched-to-conventional-commits-1p0c</link>
      <guid>https://dev.to/visuellverstehen/new-git-guidelines-we-have-switched-to-conventional-commits-1p0c</guid>
      <description>&lt;p&gt;Giving teams as much autonomy as possible is a good idea, but having some company-wide guidelines can also help. Those guidelines will help you when people switch teams, work on multiple products, or train new colleagues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discuss, decide, repeat
&lt;/h2&gt;

&lt;p&gt;We recently discussed some technology-related topics with all software developers at &lt;a href="https://www.visuellverstehen.de"&gt;visuellverstehen&lt;/a&gt;. One of the outcomes of this discussion is new git guidelines. After a short debate, we have &lt;a href="https://twitter.com/malteriechmann/status/1582599067959320577"&gt;officially&lt;/a&gt; switched to &lt;a href="https://www.conventionalcommits.org"&gt;Conventional Commits&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is fun to see all the latest commits following a new syntax. I am curious about what we will say about this change in six or twelve months. For sure we will discuss and decide again – for the better.&lt;/p&gt;

&lt;h2&gt;
  
  
  New git guidelines
&lt;/h2&gt;

&lt;p&gt;Below you can find the November 2022 edition of our git guidelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repositories
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Repositories should be named like this: &lt;code&gt;vvcode-name_of_project&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Branches
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The main branch should be named &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The people working on a product decide if they want to work with further branches&lt;/li&gt;
&lt;li&gt;If you need further branches, it is advisable, to add the number of the issue and a short title inside of a branch name, e. g.:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;100-validation-errors&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;101-basic-search&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;102-german-translation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;103-live-deployment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;104-url-formatting&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Commits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We use the syntax from Conventional Commits&lt;/li&gt;
&lt;li&gt;Commit messages include one of the following prefixes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fix:&lt;/code&gt; to fix bugs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feat:&lt;/code&gt; to introduce new features&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chore:&lt;/code&gt; to make general changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ci:&lt;/code&gt; to work on continuous integration/continuous deployment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refactor:&lt;/code&gt; to improve existing source code&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Commit messages should be written in the present form&lt;/li&gt;
&lt;li&gt;Commit messages should be written in English&lt;/li&gt;
&lt;li&gt;Commit messages should start with a lowercase character&lt;/li&gt;
&lt;li&gt;It is advisable to add the issue number to a commit message&lt;/li&gt;
&lt;li&gt;Practical examples of commit messages:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fix: display validation errors properly, refs #100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feat: implement basic search, refs #101&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chore: add correct german translation, refs #102&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ci: repair live deployment, refs #103&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;refactor: unify URL formatting, refs #104&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;If a commit is a work in progress, it should be marked as such:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fix: display validation errors properly (WIP), refs #100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feat: implement basic search (WIP), refs #101&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chore: add correct german translation (WIP), refs #102&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ci: repair live deployment (WIP), refs #103&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;refactor: unify URL formatting (WIP), refs #104&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>One year of coding – recommendations for beginners from a beginner</title>
      <dc:creator>Cleo Pelte</dc:creator>
      <pubDate>Wed, 03 Aug 2022 07:54:00 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/one-year-of-coding-recommendations-for-beginners-from-a-beginner-3cf1</link>
      <guid>https://dev.to/visuellverstehen/one-year-of-coding-recommendations-for-beginners-from-a-beginner-3cf1</guid>
      <description>&lt;p&gt;This month one year ago I started my three year long journey as a web development trainee/apprentice at visuellverstehen. I thought this would be a good opportunity to take stock and write about the most valuable lessons that I’ve learned so far &lt;del&gt;and try to overcome my fear of writing&lt;/del&gt;. Since the first year of my apprenticeship was mainly about learning the basics of front-end development, this article focuses on this specific area. Most of this is old (but gold news) but I still hope that some of my fellow beginners might find my post useful. I think especially developers who are self-taught might profit from these tips. I’m also curious to hear from other apprentices and beginners as well as the grannies what they found to be valuable during their coding journey. So please share your thoughts!&lt;br&gt;
But enough for the intro. Let’s start:&lt;/p&gt;

&lt;h2&gt;
  
  
  Know the basics
&lt;/h2&gt;

&lt;p&gt;Knowing HTML, CSS and JavaScript really well is important for building a robust and clean website. The goal should always be to never channel &lt;a href="https://youtu.be/RaQdd8bUaXk?t=658"&gt;the power of the duck&lt;/a&gt;. What also helps is to know how the web works. I am in the luxurious position to attend school twice a year for six weeks learning everything from hardware over networking to programming paradigms – it really changes the way you think about coding. And even before I went to school my instructor stressed &lt;a href="https://dev.to/visuellverstehen/but-first-learn-the-basics-1113"&gt;the importance of knowing the basics&lt;/a&gt;. It seems a little bit too much for some, - you still just want to code, right ? - but it really helps in the long run. It helps with getting a general idea of what exactly you are doing, it helps with making more informed decisions, working with clients IT departments and of course with backend matters in general.&lt;/p&gt;

&lt;p&gt;For people who are new to computer science, there is a &lt;a href="https://www.youtube.com/watch?v=tpIctyqH29Q&amp;amp;list=PLH2l6uzC4UEW0s7-KewFLBC1D0l6XRfye"&gt;crash course series&lt;/a&gt; providing a good overview of all the topics but you should definitely look further into them along the way.&lt;br&gt;
Also if you don’t know how to use the terminal yet, use it! It’s so much fun and you can spare your computer from useless user interface waste. &lt;/p&gt;

&lt;h2&gt;
  
  
  Learn about coding principles
&lt;/h2&gt;

&lt;p&gt;I love myself a &lt;a href="https://vpodk.medium.com/principles-of-software-engineering-6b702faf74a6"&gt;SOLID DRY KISS&lt;/a&gt;. These are acronyms for some of the coding principles. Coding principles help you to follow some kind of guideline, instead of coding ahead with no structure. You don’t have to follow them religiously, sometimes you have to weigh them against each other but either way they help you sensitize yourself for writing (c)lean, understandable and code that’s easy to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with proper HTML
&lt;/h2&gt;

&lt;p&gt;For every project I begin with laying down the structure of a project using plain HTML, not thinking about design and certainly no &lt;code&gt;div&lt;/code&gt;s! Just semantic HTML. It requires a deeper engagement with a project's content to find the right HTML-tags(&lt;a href="https://www.smashingmagazine.com/2022/07/article-section-elements-accessibility/"&gt;need help with section vs. article?&lt;/a&gt;) and you can easily &lt;a href="https://validator.w3.org/"&gt;validate&lt;/a&gt; your HTML after this step, &lt;a href="https://www.loginradius.com/blog/engineering/w3c-validation/#:~:text=W3C%20validation%20is%20the%20process,to%20poor%20formatting%20and%20readability."&gt;which you should always do&lt;/a&gt;. But why should you really care about a good and solid semantic structure, when you could just mix and match and &lt;code&gt;div&lt;/code&gt; it all away? First of all it might happen that &lt;a href="https://www.htmhell.dev/"&gt;the whole internet laughs at you&lt;/a&gt;. Secondly, semantic HTML helps with SEO. Thirdly, some HTML-Tags come with built-in functionalities like &lt;code&gt;button&lt;/code&gt; or &lt;code&gt;dialog&lt;/code&gt;, which you would otherwise have to add in later with JavaScripts, adding unnecessary loading time to your project as well as making your code more prone to errors. And last but not least, because …&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility matters
&lt;/h2&gt;

&lt;p&gt;Visually impaired users use assistive technology to navigate a webpage. Semantic HTML provides further information about the written content to these screen readers, that are conveyed visually for sighted users. For example is the written text a link, a button or part of a list?&lt;br&gt;
When I delved into the topic of accessibility, I was surprised how unaccessible the web still is. It’s weird, considering that I (an abled user) spend up to 12 hours a day on the internet communicating, shopping, socializing, getting information, planning and working. 5 billion people around the world have a disability and most of us who are abled, will develop some form of disability in their lifetime due to illness, accidents and age.&lt;br&gt;
I know diving into the topic can be overwhelming at first – I am still trying to figure out most basics myself –  but if you start early and sensitize yourself for this topic you’ll save time in the long run. I emphasize this aspect because when you talk about accessibility, there is always the question of: who pays for it? But if applying accessible ways to provide content becomes a habit, it will take less and less time. Also accessibility should never be thought of as an add-on, I think it’s more a question of: is your page generally usable or not? I would suggest adding an accessibility step after every main step of the project: HTML, accessibility, CSS, accessibility, JavaScript, accessibility... &lt;/p&gt;

&lt;p&gt;If you want to know more about this topic, I'd suggest watching &lt;a href="https://www.youtube.com/watch?v=are7ZZgA86I&amp;amp;t=405s&amp;amp;ab_channel=Codegram"&gt;this video about accessibility&lt;/a&gt; by Sara Soueidan, for more details on how to apply concrete measures in every front-end step, read the &lt;a href="https://medium.com/alistapart/writing-html-with-accessibility-in-mind-a62026493412"&gt;»accessibility in mind«&lt;/a&gt; series by Manuel Matuzović. I promise, you’ll know all of the important community members and their a11y content in no-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure your CSS
&lt;/h2&gt;

&lt;p&gt;After I lay down the HTML foundation and add accessibility, I give away classnames. I use the &lt;a href="http://getbem.com/introduction/"&gt;BEM&lt;/a&gt; method to give my code structure, make it reusable and understandable for other devs. Only after this step, I start styling and finally adding the &lt;code&gt;div&lt;/code&gt;s I need to translate the design.&lt;br&gt;
I’m still unsure about CSS in many situations. That may be because programming languages feel much more structured. The answers on CSS issues I run into are often semi-satisfying or even confusing. But CSS has become much more structured in recent years with &lt;code&gt;grid&lt;/code&gt;, &lt;code&gt;flex&lt;/code&gt;, CSS variables and functions like &lt;code&gt;clamp()&lt;/code&gt;, &lt;code&gt;min()&lt;/code&gt; and &lt;code&gt;max()&lt;/code&gt; etc. So I’d recommend learning how to use them consciously (there are even fun ways to learn &lt;a href="https://cssgridgarden.com/#de"&gt;grid&lt;/a&gt; and &lt;a href="https://mastery.games/flexboxzombies/"&gt;flex-box&lt;/a&gt; and &lt;a href="https://flukeout.github.io/"&gt;CSS selectors&lt;/a&gt;. Here is a good &lt;a href="https://www.youtube.com/watch?v=8slZJrTK3nE&amp;amp;t=6s"&gt;talk about CSS functions&lt;/a&gt;). &lt;br&gt;
Furthermore, applying the concepts of DRY and KISS helps your code to stay clean, readable, understandable and maintainable. For example, reduce to a maximum of 4 font-sizes to use throughout a project, even when the design might use more. Regarding larger widths, heights and spacing ask yourself: Why did you choose this sizing? Round up, round down. Did you match it by eye? If you did, try to refactor to get a clear understanding why you chose a certain sizing, or ask your designer for changes, e. g. the proportions of a SVG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin.
&lt;/h2&gt;

&lt;p&gt;That’s about it for now. I hope this was useful to some of you. I have yet to learn to apply them better myself and as I said, I’d love to hear from fellow developers what they find most valuable. The bottom line for me is: Structure and intention are so important to create a good product. &lt;br&gt;
Lastly I have to say it was a year of ups and downs, but there were mostly ups thanks to my amazing instructor, my supportive colleagues and my lovely classmates. There is so much more to learn and I’m so excited for what is to come.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Our tech stack in 2022</title>
      <dc:creator>Malte Riechmann</dc:creator>
      <pubDate>Fri, 25 Mar 2022 17:25:37 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/our-tech-stack-in-2022-374o</link>
      <guid>https://dev.to/visuellverstehen/our-tech-stack-in-2022-374o</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Reading about our &lt;a href="https://happy-coding.visuellverstehen.de/posts/our-tech-stack-in-2021-2600"&gt;tech stack from one year ago&lt;/a&gt; gives me confidence for the future. Most of the languages and frameworks are still the same as twelve months ago. I think it is good we are not switching technology that often. It means we can maintain everything for a long time and get a lot of experience in our day-to-day tools.&lt;/p&gt;

&lt;p&gt;Of course, we also have to keep an eye on the new stuff in web development and make some innovation happen at visuellverstehen. Therefore we try out new things in real-life projects from time to time. For example, I am happy there is Meilisearch now in our tech stack of 2022.&lt;/p&gt;

&lt;p&gt;As always, I recommend not rushing into the last NewShinyFramework™. &lt;/p&gt;

&lt;h2&gt;
  
  
  Clarification
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;This will not cover every technology in all of our projects, because individual projects do need individual solutions. But it will cover all the basics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Legacy projects might use outdated technologies and those will not be part of this. Of course, we always try to update legacy projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is not easy to categorize every technology. Therefore the categorization might not always be 100 % correct.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not every one of us is working with all of the technologies mentioned below. Our team is organized into smaller sub-teams, which then focus on different projects.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Our tech stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core products
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Individual digital products using &lt;a href="https://laravel.com"&gt;Laravel&lt;/a&gt; and &lt;a href="https://vuejs.org"&gt;Vue.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Content management products using &lt;a href="https://statamic.com"&gt;Statamic&lt;/a&gt; and &lt;a href="https://typo3.org"&gt;TYPO3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What people call »Backend«
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Extbase&lt;/li&gt;
&lt;li&gt;Laravel&lt;/li&gt;
&lt;li&gt;Laravel Nova&lt;/li&gt;
&lt;li&gt;Meilisearch&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;PHP&lt;/li&gt;
&lt;li&gt;PHPUnit&lt;/li&gt;
&lt;li&gt;Statamic&lt;/li&gt;
&lt;li&gt;TYPO3&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What people call »Frontend«
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Alpine.js&lt;/li&gt;
&lt;li&gt;Antlers&lt;/li&gt;
&lt;li&gt;BEM&lt;/li&gt;
&lt;li&gt;Babel&lt;/li&gt;
&lt;li&gt;Blade&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;Fluid&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Laravel Livewire&lt;/li&gt;
&lt;li&gt;Sass&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Vue.js&lt;/li&gt;
&lt;li&gt;gulp.js&lt;/li&gt;
&lt;li&gt;npm&lt;/li&gt;
&lt;li&gt;webpack&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What people call »DevOps«
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;GitLab&lt;/li&gt;
&lt;li&gt;GitLab CI&lt;/li&gt;
&lt;li&gt;Hetzner Cloud&lt;/li&gt;
&lt;li&gt;Laravel Forge&lt;/li&gt;
&lt;li&gt;Mittwald&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Honorable mentions
&lt;/h3&gt;

&lt;p&gt;Three things are worth a special mention.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Although Shopware 6 is a well-crafted software, we decided against it. We want to focus on individual digital products using Laravel and Vue.js plus content management using Statamic and TYPO3. We will still maintain running client projects and support our &lt;a href="https://store.shopware.com/en/visuellverstehen-gmbh.html"&gt;Shopware plugins&lt;/a&gt; though.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One of our clients asked for &lt;a href="https://alpinejs.dev"&gt;Alpine.js&lt;/a&gt; in combination with Laravel Livewire. It turns out those work well together. Let us see if we want to use it more often in the future.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are using Meilisearch for advanced search technologies more and more. It is a simple alternative to Solr and Elasticsearch written in Rust. It works smoothly with Laravel and Statamic. We also developed a &lt;a href="https://github.com/visuellverstehen/t3meilisearch"&gt;TYPO3 extension for Meilisearch&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Happy coding
&lt;/h2&gt;

&lt;p&gt;I am curious how this will change in the next twelve months. Well, we will find out. See you next year. Happy coding.&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>devops</category>
      <category>technology</category>
      <category>stack</category>
    </item>
    <item>
      <title>How to add pagination to your custom relationship fieldtype in Statamic</title>
      <dc:creator>Julia Lange</dc:creator>
      <pubDate>Wed, 23 Feb 2022 18:54:27 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/how-to-add-pagination-to-your-custom-relationship-fieldtype-in-statamic-54h8</link>
      <guid>https://dev.to/visuellverstehen/how-to-add-pagination-to-your-custom-relationship-fieldtype-in-statamic-54h8</guid>
      <description>&lt;p&gt;Usually when building a custom relationship fieldtype you want to work with data from somewhere else than Statamic. &lt;/p&gt;

&lt;p&gt;In this example we extended the relationship fieldtype in order to provide a list of imported tags. Thanks to the &lt;a href="https://statamic.dev/extending/relationship-fieldtypes" rel="noopener noreferrer"&gt;Statamic docs&lt;/a&gt;, this is pretty easy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tags&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Relationship&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch tags from api. &lt;/span&gt;
    &lt;span class="c1"&gt;// Using a facade makes it nice to read and easier to test.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Api&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="c1"&gt;// Define columns.&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getColumns&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Take search requests into account. &lt;/span&gt;
    &lt;span class="c1"&gt;// Just comparing strings. Nothing fancy.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getIndexItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In many cases this would already be it. But what if you have a huge amount of tags or items that need to be loaded into Statamics &lt;code&gt;selector stack&lt;/code&gt;?&lt;br&gt;
I found that loading about 16000 tags at once into the selector stack would either cause unbearable loading times or  even make the browser freeze.&lt;/p&gt;

&lt;p&gt;This issue could be solved by enabling pagination. But unfortunately the relationship fieldtype does not automatically enable pagination when loading a specific number of items.&lt;/p&gt;

&lt;p&gt;In order to do so and show pagination at the bottom of the selector stack you need to return an instance of the &lt;code&gt;\Statamic\Extensions\Pagination\LengthAwarePaginator&lt;/code&gt; instead of a collection in the &lt;code&gt;getIndexItems&lt;/code&gt; method. &lt;/p&gt;

&lt;p&gt;This could be done like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getIndexItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;LengthAwarePaginator&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$currentPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Paginator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resolveCurrentPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$perPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$total&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$tags&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;forPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$perPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LengthAwarePaginator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$perPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Curios about the LengthAwarePaginator? Read more in the &lt;a href="https://laravel.com/docs/8.x/pagination#manually-creating-a-paginator" rel="noopener noreferrer"&gt;Laravel Docs&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Et voilà - a custom relationship fieldtype with added pagination: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75kjlxwsn8q9fzmw4zqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75kjlxwsn8q9fzmw4zqj.png" alt="A custom relationship fieldtype with added pagination in Statamic"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>statamic</category>
      <category>extending</category>
      <category>fieldtype</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Common mistakes when writing CSS with BEM</title>
      <dc:creator>Malte Riechmann</dc:creator>
      <pubDate>Fri, 22 Oct 2021 08:48:47 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/common-mistakes-when-writing-css-with-bem-4921</link>
      <guid>https://dev.to/visuellverstehen/common-mistakes-when-writing-css-with-bem-4921</guid>
      <description>&lt;h2&gt;
  
  
  Software development is a team effort
&lt;/h2&gt;

&lt;p&gt;When doing software development it is essential to agree on guidelines, technology, and methodologies. Those agreements should be the result of discussions, proof of concepts, knowledge, and sometimes votes. The whole team should be invited to participate because software development is a team effort and everyone likes engaging team members.&lt;/p&gt;

&lt;p&gt;At visuellverstehen, we are divided into multiple teams. Some teams agreed on using &lt;a href="https://css-tricks.com/bem-101"&gt;Block-Element-Modifier (BEM)&lt;/a&gt; and other teams agreed on using &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;. I think it is super important to agree on one way or the other, while both ways are totally fine for the success of our client projects.&lt;/p&gt;

&lt;p&gt;One thing is for sure. If you do not agree, it will become a mess. I have been there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn proper BEM
&lt;/h2&gt;

&lt;p&gt;Now and then new colleagues join our team. Some of them never have heard about BEM before. So they have to learn it and naturally some mistakes will happen. Mistakes are not a problem at all. They are part of the learning process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four most common mistakes
&lt;/h2&gt;

&lt;p&gt;To help you learn proper BEM, I wrote down some of the most common mistakes I see in my day-to-day work life.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Wrongly nested blocks and elements
&lt;/h3&gt;

&lt;p&gt;It is not allowed to nest blocks. If you start a new block, you are not allowed to proceed with elements from another block.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❌ Wrong
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Correct
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Great-grandchildren
&lt;/h3&gt;

&lt;p&gt;There are no grandchildren nor great-grandchildren in BEM. Instead, »normal« elements of the block can be used.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❌ Wrong
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header__headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Correct
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Modifiers without a base class
&lt;/h3&gt;

&lt;p&gt;Modifiers cannot exist without a base block or element.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❌ Wrong
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card--highlight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Correct
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card card--highlight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ❌ Wrong
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header--important"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Correct
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__header card__header--important"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Too big blocks
&lt;/h3&gt;

&lt;p&gt;It is not a good idea to create really big blocks. The idea of BEM is to create modular and reusable blocks.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❌ Wrong
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body__header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body__main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body__footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅ Correct
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automate stuff
&lt;/h2&gt;

&lt;p&gt;Sometimes it is hard to find mistakes manually. Yesterday I learned there is a &lt;a href="https://github.com/postcss/postcss-bem-linter"&gt;BEM linter&lt;/a&gt;. I will look into it.&lt;/p&gt;

</description>
      <category>css</category>
      <category>programming</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A little Slack app to ask our team social questions</title>
      <dc:creator>Malte Riechmann</dc:creator>
      <pubDate>Thu, 10 Jun 2021 09:10:46 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/a-little-slack-app-to-ask-our-team-social-questions-4n83</link>
      <guid>https://dev.to/visuellverstehen/a-little-slack-app-to-ask-our-team-social-questions-4n83</guid>
      <description>&lt;h2&gt;
  
  
  Changes coming from the pandemic
&lt;/h2&gt;

&lt;p&gt;A lot has changed since the pandemic started and we all can learn a lot from the past 18 months. Because of COVID-19, we are now a remote workplace. Which is very nice, but also brings some challenges.&lt;/p&gt;

&lt;p&gt;For example, some people from our team have never met in person. While working remotely, you do not occasionally run into someone and have to talk to him*her. The social talk decreased a lot. While it is okay for some, others want it back. How can we counteract this?&lt;/p&gt;

&lt;h2&gt;
  
  
  A little idea
&lt;/h2&gt;

&lt;p&gt;We do not want to bring the office back and we do not want to add a lot of distraction. We just want to get to know each other a bit better. So we developed an app, which asks our team a social question every Monday at 1 pm. Those questions are posted to an off-topic channel, which is optional for everyone to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions to ask
&lt;/h2&gt;

&lt;p&gt;We gathered questions and put those into a JSON file. Sorry for using German here, but those are some real questions our team was asked in the last months.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":palm_tree: An welchem Ort machst du am liebsten Urlaub? Vielleicht kannst du ja auch ein Foto teilen."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":tv: Was ist deine Lieblingsserie? Und wo können die anderen diese Serie gucken?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":green_salad: Welches Essen ist so gar nichts für dich?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":musical_note: Welche Musik hörst du, wenn du einfach nur abschalten willst?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":frame_with_picture: Wie hast du als Kind ausgesehen?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":moneybag: Was meinst du? Wird Bitcoin die Währung der Zukunft?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":children_crossing: Was ist deine schlimmste Jugendsünde?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":movie_camera: Was ist dein Lieblingskinderfilm?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":tropical_drink: Zitronen- oder Pfirsich-Eistee?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":desert_island: Welche drei Dinge würdest du mit auf eine einsame Insel nehmen?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":bread: Der, die oder das Nutella?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;":icecream: Was ist deine Lieblingseissorte und warum?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"[…]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;The app is just a PHP script, which is run by a cronjob once a week. It works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a question from &lt;code&gt;questions.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Post the question using the &lt;a href="https://api.slack.com/methods/chat.postMessage"&gt;chat.postMessage&lt;/a&gt; API endpoint.&lt;/li&gt;
&lt;li&gt;Add the question to &lt;code&gt;questions-archive.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Remove the question from &lt;code&gt;questions.json&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;#!/usr/local/bin/php
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// Abort, if not in CLI mode&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;php_sapi_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'cli'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Not in CLI mode'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Prepare some variables&lt;/span&gt;
&lt;span class="nv"&gt;$archivedQuestionsFilename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'questions-archive.json'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$questionsFilename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'questions.json'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Read questions from file&lt;/span&gt;
&lt;span class="nv"&gt;$questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$questionsFilename&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Abort, if no questions are left&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'No questions left in '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$questionsFilename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Send question via cURL to Slack&lt;/span&gt;
&lt;span class="nv"&gt;$curlHandle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$sendQuestionUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://slack.com/api/chat.postMessage'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$sendQuestionParameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'channel'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'C03C9RPDF'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'text'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Hallo Freund*innen der gepflegten Unterhaltung.

'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$sendQuestionBearerToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0123456789ABCDEF'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$sendQuestionBearerToken&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$sendQuestionUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sendQuestionParameters&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$sendQuestionResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;curl_close&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Read archived questions from file&lt;/span&gt;
&lt;span class="nv"&gt;$archivedQuestions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$archivedQuestionsFilename&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Add question to archived questions&lt;/span&gt;
&lt;span class="nv"&gt;$archivedQuestions&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Remove question from questions&lt;/span&gt;
&lt;span class="k"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Persist archived questions&lt;/span&gt;
&lt;span class="nb"&gt;file_put_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$archivedQuestionsFilename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;array_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$archivedQuestions&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="c1"&gt;// Persist questions&lt;/span&gt;
&lt;span class="nb"&gt;file_put_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$questionsFilename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;array_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>php</category>
      <category>programming</category>
    </item>
    <item>
      <title>This article has 61 positive reactions and 24 comments</title>
      <dc:creator>Malte Riechmann</dc:creator>
      <pubDate>Fri, 04 Jun 2021 07:43:51 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/this-article-has-positive-reactions-and-comments-ol3</link>
      <guid>https://dev.to/visuellverstehen/this-article-has-positive-reactions-and-comments-ol3</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Using the DEV API the title of this article gets automatically updated every 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addicted to numbers
&lt;/h2&gt;

&lt;p&gt;I started blogging on DEV only some months ago. You could say, I am quite new to all of this. After writing an article I find myself frequently checking the numbers of reactions and comments. It seems like I am a bit of an addict. And I bet some of you are too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We share an addiction. We're approval junkies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— &lt;a href="https://www.youtube.com/watch?v=GkEE3gRX0kQ"&gt;Jake Green, Revolver&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is not why I started this and I am sure it is not healthy. So I will try to stop and instead make this a bit of fun. Let's play around with the numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs are the future
&lt;/h2&gt;

&lt;p&gt;Back in 2010, I saw a &lt;a href="https://twitter.com/smashingmag/status/9646126079"&gt;tweet&lt;/a&gt; from Smashing Magazine asking about the future of the web. And I answered »&lt;a href="https://twitter.com/malteriechmann/status/9646229572"&gt;APIs&lt;/a&gt;«, which is the same answer I would give today — 11 years later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's have fun
&lt;/h2&gt;

&lt;p&gt;It is so much fun working with well-implemented APIs and I was happy to find the &lt;a href="https://docs.forem.com/api/"&gt;DEV API&lt;/a&gt; as one of those.&lt;/p&gt;

&lt;p&gt;My idea was simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the properties of this article.&lt;/li&gt;
&lt;li&gt;Update the title using two of the properties (&lt;code&gt;positive_reactions_count&lt;/code&gt; and &lt;code&gt;comments_count&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The source
&lt;/h2&gt;

&lt;p&gt;I use PHP, which is one of my favorite programming languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get article properties
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getArticleProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$articleId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Prepare URL&lt;/span&gt;
    &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://dev.to/api/articles/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$articleId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Prepare headers&lt;/span&gt;
    &lt;span class="nv"&gt;$headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'api-key: 1234567890abcdef'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Prepare method&lt;/span&gt;
    &lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute request&lt;/span&gt;
    &lt;span class="nv"&gt;$curlHandle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_CUSTOMREQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update article title
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateArticleTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$articleId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$articleTitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Prepare URL&lt;/span&gt;
    &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://dev.to/api/articles/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$articleId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Prepare payload&lt;/span&gt;
    &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'article'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$articleTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Prepare headers&lt;/span&gt;
    &lt;span class="nv"&gt;$headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'Content-Length: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'api-key: 1234567890abcdef'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Prepare method&lt;/span&gt;
    &lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PUT'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute request&lt;/span&gt;
    &lt;span class="nv"&gt;$curlHandle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_CUSTOMREQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curlHandle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Prepare article ID&lt;/span&gt;
&lt;span class="nv"&gt;$articleId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;715066&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Get article properties using the API&lt;/span&gt;
&lt;span class="nv"&gt;$articleProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getArticleProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$articleId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Update article title using the API&lt;/span&gt;
&lt;span class="nf"&gt;updateArticleTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$articleId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'This article has '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$articleProperties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'positive_reactions_count'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' positive reactions and '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$articleProperties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'comments_count'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' comments'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cronjob is executing this as a CLI script every 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;This article is heavily inspired by &lt;a href="https://www.youtube.com/watch?v=BxV14h0kFs0"&gt;an awesome YouTube video&lt;/a&gt; I saw earlier this year.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>writing</category>
      <category>meta</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Use components without a frontend framework</title>
      <dc:creator>Malte Riechmann</dc:creator>
      <pubDate>Thu, 27 May 2021 11:49:25 +0000</pubDate>
      <link>https://dev.to/visuellverstehen/use-components-without-a-frontend-framework-4338</link>
      <guid>https://dev.to/visuellverstehen/use-components-without-a-frontend-framework-4338</guid>
      <description>&lt;h2&gt;
  
  
  Frontend frameworks
&lt;/h2&gt;

&lt;p&gt;Within this post, I will show you one, of many ways to use components without a frontend framework. Do not get me wrong, I like frameworks like Vue.js, React, or Angular. At our company, we write JavaScript with Vue.js on a daily basis.&lt;/p&gt;

&lt;p&gt;But sometimes those frameworks are too much. Especially when building simple websites instead of complex web applications. In these cases, we do not use a framework at all and just write HTML, CSS, and JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;If your source code gets messy there is often an easy way to improve it. Move your source code into smaller chunks – &lt;a href="https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm" rel="noopener noreferrer"&gt;divide and conquer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Splitting your source code into multiple components is a well-known way to structure your project. Things get isolated, readable, clear, reusable, extendable, and maintainable. In fact, that is what most of the frontend frameworks do themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome to BEM
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/bem-101/" rel="noopener noreferrer"&gt;BEM (Block Element Modifier)&lt;/a&gt; is a methodology to organize your frontend. It is mostly known in the world of CSS, but I am going to show you how to use it for JavaScript, too. Please remember, BEM is a methodology and not a framework. It will come with zero dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  A card block
&lt;/h2&gt;

&lt;p&gt;In BEM the components are called blocks. It is just different terminology for the same thing.&lt;/p&gt;

&lt;p&gt;Now, imagine a card block (&lt;a href="https://jsfiddle.net/3stx2jqa/" rel="noopener noreferrer"&gt;source code&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26ltrld7bskr6nd9sl4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26ltrld7bskr6nd9sl4g.png" alt="Example of a card block"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;We use HTML for the structure and content of the card block. Of course, you can use programming languages, template engines, or other tools to improve the HTML part of the block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card card--highlight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;figure&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__figure"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__image"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;figcaption&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__caption"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;#ffed00&lt;span class="nt"&gt;&amp;lt;/figcaption&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/figure&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Corporate yellow&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This yellow is defined as #ffed00. It is a very nice color. It is one of our corporate colors at visuellverstehen.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CSS
&lt;/h3&gt;

&lt;p&gt;We use CSS for the presentation and style of the card block. Of course, you can use pre processors, post processors, or other tools to make CSS more comfortable to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#f6f6f6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card.card--highlight&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffed00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card__figure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card__image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card__caption&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card__headline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;

&lt;p&gt;We use JavaScript for the functionality of the card block. Of course, you can use all kinds of tools to improve how you write JavaScript (Babel, TypeScript, ESLint, webpack, …).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;initializeCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Do whatever this $card block should be doing.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;initializeCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;}());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes blocks have to communicate with each other. For that, there are at least two good options: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent" rel="noopener noreferrer"&gt;Custom events&lt;/a&gt; and &lt;a href="https://css-tricks.com/build-a-state-management-system-with-vanilla-javascript/" rel="noopener noreferrer"&gt;stage management&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn the basics
&lt;/h2&gt;

&lt;p&gt;It is remarkable how much can be done just using HTML, CSS, and JavaScript. That is one reason why I encourage everyone to &lt;a href="https://dev.to/visuellverstehen/but-first-learn-the-basics-1113"&gt;learn the basics&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
