<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Emil Moe]]></title><description><![CDATA[*Data Engineer specialised in PySpark and webworks in Laravel and Vue*]]></description><link>https://blog.emil.moe</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 11:37:37 GMT</lastBuildDate><atom:link href="https://blog.emil.moe/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Laravel Default Models Explained]]></title><description><![CDATA[In Laravel, Eloquent ORM facilitates defining relationships between models, such as a Post belonging to an Author. Typically, if a Post's Author is missing, accessing the author results in an error. Default models address this issue.
By implementing ...]]></description><link>https://blog.emil.moe/laravel-default-models-explained</link><guid isPermaLink="true">https://blog.emil.moe/laravel-default-models-explained</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Mon, 10 Jun 2024 09:19:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718011068219/df51daaa-5c02-4b5b-b750-17907889e11a.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Laravel, Eloquent ORM facilitates defining relationships between models, such as a <code>Post</code> belonging to an <code>Author</code>. Typically, if a <code>Post</code>'s <code>Author</code> is missing, accessing the author results in an error. Default models address this issue.</p>
<p>By implementing the <code>withDefault()</code> method, you ensure your application returns a default model when a related model is absent. This prevents errors and allows you to define default attribute values for the related model.</p>
<h4 id="heading-practical-example">Practical Example</h4>
<p>Imagine you have a <code>Post</code> model linked to an <code>Author</code> model. Without default models, attempting to access the author of a post with no associated author leads to an error:</p>
<pre><code class="lang-php">$post = Post::find(<span class="hljs-number">1</span>);
<span class="hljs-keyword">echo</span> $post-&gt;author-&gt;name; <span class="hljs-comment">// Error if author is missing</span>
</code></pre>
<p>Using <code>withDefault()</code>, you can handle this situation more effectively:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">author</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;belongsTo(Author::class)-&gt;withDefault();
}
</code></pre>
<p>Now, if the author is missing, an empty <code>Author</code> model is returned instead of an error. Additionally, you can specify default attribute values:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">author</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;belongsTo(Author::class)-&gt;withDefault([
        <span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">'Guest Author'</span>,
    ]);
}
</code></pre>
<p>This way, if an author is not set, your application will use "Guest Author" as the default name, ensuring a seamless user experience.</p>
<h4 id="heading-benefits-of-using-default-models">Benefits of Using Default Models</h4>
<ol>
<li><p><strong>Error Prevention</strong>: Avoid runtime errors due to missing related models.</p>
</li>
<li><p><strong>Improved User Experience</strong>: Provide meaningful default values, ensuring your application remains informative and user-friendly even when data is incomplete.</p>
</li>
<li><p><strong>Cleaner Code</strong>: Simplify your view logic by eliminating the need for null checks every time you access related models.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Setting Up a Docker-Hosted Droplet on DigitalOcean Using Terraform: A Step-by-Step Guide]]></title><description><![CDATA[Welcome to the world of cloud infrastructure, where ease of deployment and scalability are paramount. Today, we're going to walk through setting up a Droplet (a virtual server) on DigitalOcean, one of the most user-friendly cloud platforms. Our focus...]]></description><link>https://blog.emil.moe/setting-up-a-docker-hosted-droplet-on-digitalocean-using-terraform-a-step-by-step-guide</link><guid isPermaLink="true">https://blog.emil.moe/setting-up-a-docker-hosted-droplet-on-digitalocean-using-terraform-a-step-by-step-guide</guid><category><![CDATA[Terraform]]></category><category><![CDATA[DigitalOcean]]></category><category><![CDATA[Cloud Computing]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Thu, 04 Jan 2024 13:47:30 GMT</pubDate><content:encoded><![CDATA[<p>Welcome to the world of cloud infrastructure, where ease of deployment and scalability are paramount. Today, we're going to walk through setting up a Droplet (a virtual server) on DigitalOcean, one of the most user-friendly cloud platforms. Our focus will be on hosting Docker containers within this Droplet, all orchestrated using Terraform, a popular infrastructure as code tool.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before we start, ensure you have the following:</p>
<ol>
<li><p><strong>A DigitalOcean Account</strong>: Sign up <a target="_blank" href="https://www.digitalocean.com/">here</a> if you haven’t already.</p>
</li>
<li><p><strong>Terraform Installed</strong>: Follow the <a target="_blank" href="https://learn.hashicorp.com/tutorials/terraform/install-cli">installation guide</a>.</p>
</li>
<li><p><strong>Docker Knowledge</strong>: Basic understanding of Docker and containerization.</p>
</li>
<li><p><strong>DigitalOcean API Token</strong>: Generate this from your DigitalOcean account for Terraform to interact with DigitalOcean's API.</p>
</li>
</ol>
<h3 id="heading-step-1-setting-up-terraform">Step 1: Setting Up Terraform</h3>
<p>First, create a directory for your Terraform project:</p>
<pre><code class="lang-bash">mkdir terraform-digitalocean-droplet
<span class="hljs-built_in">cd</span> terraform-digitalocean-droplet
</code></pre>
<p>Create a file named <a target="_blank" href="http://main.tf"><code>main.tf</code></a>. This file will define your Terraform configuration.</p>
<pre><code class="lang-python">provider <span class="hljs-string">"digitalocean"</span> {
  token = <span class="hljs-string">"your_digitalocean_api_token"</span>
}
</code></pre>
<p>Replace <code>your_digitalocean_api_token</code> with your actual API token.</p>
<h3 id="heading-step-2-defining-the-droplet-in-terraform">Step 2: Defining the Droplet in Terraform</h3>
<p>Now, define your Droplet resource in the <a target="_blank" href="http://main.tf"><code>main.tf</code></a> file:</p>
<pre><code class="lang-python">resource <span class="hljs-string">"digitalocean_droplet"</span> <span class="hljs-string">"docker_host"</span> {
  image  = <span class="hljs-string">"docker-18-04"</span>
  name   = <span class="hljs-string">"docker-droplet"</span>
  region = <span class="hljs-string">"nyc3"</span>
  size   = <span class="hljs-string">"s-1vcpu-1gb"</span>
}
</code></pre>
<p>This configuration creates a Droplet named <code>docker-droplet</code> in the NYC3 region with a basic size. The image is set to <code>docker-18-04</code>, a DigitalOcean image preconfigured with Docker.</p>
<h3 id="heading-step-3-creating-a-volume-for-docker">Step 3: Creating a Volume for Docker</h3>
<p>To store Docker data, let's add a volume to our Droplet. In <a target="_blank" href="http://main.tf"><code>main.tf</code></a>, add:</p>
<pre><code class="lang-python">resource <span class="hljs-string">"digitalocean_volume"</span> <span class="hljs-string">"docker_volume"</span> {
  region      = <span class="hljs-string">"nyc3"</span>
  name        = <span class="hljs-string">"docker-volume"</span>
  size        = <span class="hljs-number">10</span>
  initial_filesystem_type = <span class="hljs-string">"ext4"</span>
  description = <span class="hljs-string">"Volume for Docker containers"</span>
}

resource <span class="hljs-string">"digitalocean_volume_attachment"</span> <span class="hljs-string">"docker_volume_attachment"</span> {
  droplet_id = digitalocean_droplet.docker_host.id
  volume_id   = digitalocean_volume.docker_volume.id
}
</code></pre>
<p>This code creates a 10GB volume and attaches it to our Droplet.</p>
<h3 id="heading-step-4-initializing-and-applying-terraform-configuration">Step 4: Initializing and Applying Terraform Configuration</h3>
<p>Initialize your Terraform setup:</p>
<pre><code class="lang-bash">terraform init
</code></pre>
<p>Then, apply the configuration:</p>
<pre><code class="lang-bash">terraform apply
</code></pre>
<p>Confirm the plan by typing <code>yes</code> when prompted. Terraform will now create your Droplet and attach the volume.</p>
<h3 id="heading-step-5-accessing-and-using-your-droplet">Step 5: Accessing and Using Your Droplet</h3>
<p>Once Terraform completes, use the Droplet's IP address to access it:</p>
<pre><code class="lang-bash">ssh root@your_droplet_ip
</code></pre>
<p>Check if Docker is running:</p>
<pre><code class="lang-bash">docker info
</code></pre>
<p>Your Docker-hosted Droplet is ready! You can now deploy containers on this Droplet.</p>
<h3 id="heading-step-6-mounting-the-volume-in-docker">Step 6: Mounting the Volume in Docker</h3>
<p>Mount the attached volume in your Docker containers for persistent storage:</p>
<pre><code class="lang-bash">docker run -v /path/to/volume:/path/<span class="hljs-keyword">in</span>/container -d your_image
</code></pre>
<p>Replace <code>/path/to/volume</code> with the actual path where your volume is mounted on the Droplet, and <code>/path/in/container</code> with the path inside your container where you want the volume to be mounted.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Congratulations! You've successfully set up a Docker-hosted Droplet on DigitalOcean using Terraform. This setup not only simplifies management and deployment of your infrastructure but also leverages the power of Docker for containerization, ensuring a scalable and efficient environment.</p>
]]></content:encoded></item><item><title><![CDATA[Introduction to Terraform on DigitalOcean]]></title><description><![CDATA[Getting started with Terraform on DigitalOcean can be an exciting journey for developers and DevOps engineers looking to manage infrastructure as code. In this blog post, we'll guide you through the basics of using Terraform with DigitalOcean, helpin...]]></description><link>https://blog.emil.moe/introduction-to-terraform-on-digitalocean</link><guid isPermaLink="true">https://blog.emil.moe/introduction-to-terraform-on-digitalocean</guid><category><![CDATA[Terraform]]></category><category><![CDATA[DigitalOcean]]></category><category><![CDATA[Cloud Computing]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Thu, 04 Jan 2024 09:58:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704367317365/943ded5a-fe35-43ef-b947-fe76fa16a446.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Getting started with Terraform on DigitalOcean can be an exciting journey for developers and DevOps engineers looking to manage infrastructure as code. In this blog post, we'll guide you through the basics of using Terraform with DigitalOcean, helping you to deploy and manage your infrastructure efficiently and programmatically.</p>
<hr />
<p><strong>Terraform</strong> is an open-source infrastructure as code software tool created by HashiCorp. It allows users to define and provision a datacenter infrastructure using a high-level configuration language known as HashiCorp Configuration Language (HCL), or optionally JSON.</p>
<p><strong>DigitalOcean</strong> is a cloud infrastructure provider that offers cloud services to help deploy modern apps. It's known for its simplicity and being developer-friendly.</p>
<p>Combining Terraform with DigitalOcean can significantly streamline the process of deploying and managing cloud infrastructure.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ol>
<li><p><strong>Basic Knowledge of Cloud Computing:</strong> A fundamental understanding of cloud computing concepts is beneficial.</p>
</li>
<li><p><strong>DigitalOcean Account:</strong> You'll need an active DigitalOcean account. If you don't have one, you can sign up at <a target="_blank" href="https://www.digitalocean.com/">DigitalOcean</a>.</p>
</li>
<li><p><strong>Terraform Installed:</strong> Ensure you have Terraform installed on your machine. If not, you can download it from the <a target="_blank" href="https://www.terraform.io/downloads.html">Terraform website</a>.</p>
</li>
</ol>
<h3 id="heading-step-1-setting-up-terraform">Step 1: Setting Up Terraform</h3>
<p>After installing Terraform, the first step is to write a configuration file (<code>.tf</code> file) that describes the infrastructure you want to manage.</p>
<ol>
<li><p><strong>Initialize a New Terraform Directory:</strong> Create a new directory for your Terraform configuration and initialize it using the <code>terraform init</code> command. This command prepares your directory for Terraform operations.</p>
</li>
<li><p><strong>Write Your Terraform Configuration:</strong> In your Terraform directory, create a new file named <a target="_blank" href="http://main.tf"><code>main.tf</code></a>. This file will define your DigitalOcean infrastructure using HCL.</p>
</li>
</ol>
<h3 id="heading-step-2-configuring-digitalocean-provider">Step 2: Configuring DigitalOcean Provider</h3>
<p>Terraform uses providers to interact with cloud service providers. For DigitalOcean, you need to configure the DigitalOcean provider in your <a target="_blank" href="http://main.tf"><code>main.tf</code></a> file.</p>
<ol>
<li><p><strong>Provider Configuration:</strong> Include the DigitalOcean provider and your DigitalOcean API token (you can generate this token in your DigitalOcean dashboard) in your configuration. This step allows Terraform to authenticate with DigitalOcean on your behalf.</p>
<pre><code class="lang-python"> provider <span class="hljs-string">"digitalocean"</span> {
   token = <span class="hljs-string">"your_digitalocean_api_token"</span>
 }
</code></pre>
</li>
<li><p><strong>Define Resources:</strong> Define the resources (like droplets, load balancers, etc.) you want to create. For example, to create a droplet:</p>
<pre><code class="lang-python"> resource <span class="hljs-string">"digitalocean_droplet"</span> <span class="hljs-string">"web"</span> {
   image = <span class="hljs-string">"ubuntu-20-04-x64"</span>
   name = <span class="hljs-string">"web-server"</span>
   region = <span class="hljs-string">"nyc3"</span>
   size = <span class="hljs-string">"s-1vcpu-1gb"</span>
 }
</code></pre>
</li>
</ol>
<h3 id="heading-step-3-deploying-infrastructure">Step 3: Deploying Infrastructure</h3>
<ol>
<li><p><strong>Plan Your Deployment:</strong> Run <code>terraform plan</code> to review the changes Terraform will make to your infrastructure.</p>
</li>
<li><p><strong>Apply the Configuration:</strong> Execute <code>terraform apply</code> to deploy the resources as defined in your configuration file.</p>
</li>
</ol>
<h3 id="heading-step-4-managing-and-updating-your-infrastructure">Step 4: Managing and Updating Your Infrastructure</h3>
<ul>
<li><p><strong>Modifying Resources:</strong> Update your <code>.tf</code> files to modify your resources and run <code>terraform apply</code> again.</p>
</li>
<li><p><strong>Monitoring Changes:</strong> Terraform tracks the state of your managed infrastructure and configurations.</p>
</li>
</ul>
<h3 id="heading-step-5-destroying-infrastructure">Step 5: Destroying Infrastructure</h3>
<p>When you no longer need the infrastructure, you can destroy it to prevent further charges. Run <code>terraform destroy</code> to remove all resources managed by your Terraform configuration.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Terraform is a powerful tool for managing infrastructure as code, particularly when combined with a cloud provider like DigitalOcean. It offers a reproducible and efficient way to manage cloud resources, significantly simplifying the process of infrastructure deployment and management.</p>
<p>Getting started with Terraform on DigitalOcean might seem daunting at first, but with the right steps, it becomes a straightforward and rewarding process. Happy coding and managing your cloud infrastructure!</p>
]]></content:encoded></item><item><title><![CDATA[Realistic Employee Profiles using Faker in Python]]></title><description><![CDATA[In the world of software development, testing is a crucial aspect of ensuring the reliability and functionality of your applications. One common challenge in testing is generating realistic and diverse sets of data for your test cases. This is where ...]]></description><link>https://blog.emil.moe/realistic-employee-profiles-using-faker-in-python</link><guid isPermaLink="true">https://blog.emil.moe/realistic-employee-profiles-using-faker-in-python</guid><category><![CDATA[Python]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Sat, 16 Dec 2023 23:21:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/D9Zow2REm8U/upload/088446c22839315916646fe86886b48e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of software development, testing is a crucial aspect of ensuring the reliability and functionality of your applications. One common challenge in testing is generating realistic and diverse sets of data for your test cases. This is where Faker libraries come to the rescue. In Python, Faker is a widely used library that allows developers to create fake data with ease. In this post, we'll delve into the concept of Faker factories and demonstrate how they can be utilized for generating users, products, and orders in your Python applications.</p>
<h2 id="heading-what-are-faker-factories">What are Faker Factories?</h2>
<p>Faker is a Python library that generates fake data such as names, addresses, emails, and more. Faker factories take this a step further by allowing you to create custom classes to generate complex, interconnected sets of data. This is particularly useful for creating realistic datasets for testing purposes.</p>
<h2 id="heading-generating-users">Generating Users</h2>
<p>Let's start with creating a Faker factory for generating user data. First, make sure to install the Faker library:</p>
<pre><code class="lang-python">pip install faker
</code></pre>
<p>Now, let's create a simple user factory:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> faker <span class="hljs-keyword">import</span> Faker

fake = Faker()

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserFactory</span>:</span>
<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_user</span>():</span>
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'username'</span>: fake.user_name(),
            <span class="hljs-string">'email'</span>: fake.email(),
            <span class="hljs-string">'full_name'</span>: fake.name(),
            <span class="hljs-string">'password'</span>: fake.password(),
        }

<span class="hljs-comment"># Example usage:</span>
user_data = UserFactory.create_user()
print(user_data)
</code></pre>
<p>We define a <code>UserFactory</code> class with a static method <code>create_user</code>. This method is responsible for generating a dictionary representing user data. The dictionary contains keys such as 'username', 'email', 'full_name', and 'password', and the corresponding values are generated using various Faker methods (<code>fake.user_name()</code>, <a target="_blank" href="http://fake.email"><code>fake.email</code></a><code>()</code>, etc.).</p>
<p>Then we create an instance of the <code>UserFactory</code> class (<code>user_factory</code>) and use it to generate user data by calling the <code>create_user</code> method. The resulting <code>user_data</code> dictionary is then printed to the console.</p>
<p>Example 1 demonstrates the creation of a simple Faker factory (<code>UserFactory</code>) that generates fake user data using the Faker library's methods for generating names, emails, and passwords. The factory pattern allows for easy reuse and extension, making it a convenient approach for generating consistent and diverse sets of data in testing scenarios.</p>
<h2 id="heading-generating-products">Generating Products</h2>
<p>Now, let's extend our use of Faker factories to generate product data. We'll create a ProductFactory:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductFactory</span>:</span>
<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_product</span>():</span>
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'product_name'</span>: fake.word(),
            <span class="hljs-string">'price'</span>: fake.random_int(min=<span class="hljs-number">10</span>, max=<span class="hljs-number">100</span>),
            <span class="hljs-string">'description'</span>: fake.text(),
        }

<span class="hljs-comment"># Example usage:</span>
product_data = ProductFactory.create_product()
print(product_data)
</code></pre>
<p>Example 2 demonstrates the creation of another Faker factory (<code>ProductFactory</code>) specifically tailored for generating fake product data. The generated data includes a product name, a random price within a specified range, and a description.</p>
<h2 id="heading-combining-orders-for-products-and-users"><strong>Combining orders for Products and Users</strong></h2>
<p>Now, let's tie everything together by creating a factory to generate orders. Each order will include a user and a product:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderFactory</span>:</span>
<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_order</span>(<span class="hljs-params">user_factory, product_factory</span>):</span>
        user_data = user_factory.create_user()
        product_data = product_factory.create_product()
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'order_id'</span>: fake.uuid4(),
            <span class="hljs-string">'user'</span>: user_data,
            <span class="hljs-string">'product'</span>: product_data,
            <span class="hljs-string">'quantity'</span>: fake.random_int(min=<span class="hljs-number">1</span>, max=<span class="hljs-number">10</span>),
        }

<span class="hljs-comment"># Example usage:</span>
user_factory = UserFactory()
product_factory = ProductFactory()
order_data = OrderFactory.create_order(user_factory, product_factory)
print(order_data)
</code></pre>
<p>We define an <code>OrderFactory</code> class with a static method <code>create_order</code>. This method takes instances of <code>user_factory</code> and <code>product_factory</code> as parameters. It then generates an order, where the order ID is a UUID, and the user and product details are generated using the respective factories. Additionally, a random quantity is assigned to the order.</p>
<p>We create instances of <code>UserFactory</code> and <code>ProductFactory</code>. Then, we use these factories as arguments to create an order using the <code>OrderFactory</code>. The resulting <code>order_data</code> dictionary is printed to the console.</p>
<p>The <code>OrderFactory</code> not only generates order-specific information but also leverages the <code>UserFactory</code> and <code>ProductFactory</code> to create realistic user and product data for the order. This showcases the power of Faker factories in creating complex and interconnected datasets for testing scenarios.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Faker factories in Python offer a powerful way to generate realistic and interconnected datasets for testing purposes. By leveraging the flexibility of Faker and custom factories, you can efficiently create diverse sets of data for your applications. Whether you're testing user authentication, product catalog functionality, or order processing systems, Faker factories are a valuable tool in your testing toolkit.</p>
]]></content:encoded></item><item><title><![CDATA[Laravel Translatable Attributes]]></title><description><![CDATA[In order to fluently support multilingual, I created this trait for Laravel. My ambition was to make it as seamlessly integrated as possible and to follow the semantics of Laravel.
You may also choose to install it with composer:
composer require clo...]]></description><link>https://blog.emil.moe/laravel-translatable-attributes</link><guid isPermaLink="true">https://blog.emil.moe/laravel-translatable-attributes</guid><category><![CDATA[Laravel]]></category><category><![CDATA[translation]]></category><category><![CDATA[eloquent]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Wed, 01 Sep 2021 06:59:47 GMT</pubDate><content:encoded><![CDATA[<p>In order to fluently support multilingual, I created this trait for Laravel. My ambition was to make it as seamlessly integrated as possible and to follow the semantics of Laravel.</p>
<p>You may also choose to install it with composer:</p>
<pre><code>composer <span class="hljs-keyword">require</span> cloudmonitor/translatable
</code></pre><h3 id="file-location">File location</h3>
<p>As the namespace indicates, I chose to store the trait in <code>app\Models\Traits\Translatable.php</code>, but it's free for you to change this of course.</p>
<p>I have to document the necessary steps in the trait itself, to keep it in a nice single component file style concept, however, for convenience, I will walk through the steps here.</p>
<h3 id="prepare-eloquent-models">Prepare Eloquent models</h3>
<p>Similar to other special attributes in Eloquent, such as <code>$fillables</code>, translatable attributes must be defined as an array. It is as simple as giving the name of the database column:</p>
<pre><code><span class="hljs-keyword">protected</span> $translatable = [
    <span class="hljs-string">'name'</span>,
];
</code></pre><p>Translatable will now only be observing these attributes and skip the rest.</p>
<h3 id="using-translations">Using translations</h3>
<p>As Translatable uses Laravels <code>app()-&gt;getLocale()</code> it means it will figure out which language to use when you query name.</p>
<p>For instance, your locale is currently <code>da</code> (Danish), so you want to update a book title. Simply do it as there was no translation implementation:</p>
<pre><code>$book = Book::find($id);
$book-&gt;name = <span class="hljs-string">'New name for Danish version'</span>;
$book-&gt;save();
</code></pre><p>Or as an update method:</p>
<pre><code>Book::find($id)-&gt;update([<span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">'New name for Danish version'</span>]);
</code></pre><p>Similar when you want to get the name in the current locale you simply query it:</p>
<pre><code><span class="hljs-keyword">return</span> Book::find($id)-&gt;name;
</code></pre><h4 id="other-locales">Other locales</h4>
<p>Sometimes you want to update all translations or in a specific language or simply in a different than you are using. It could be a Danish moderator who wants to update the English title, titles for several languages, or something different.</p>
<pre><code>Book::find($id)-&gt;setTranslation(<span class="hljs-string">'name'</span>, <span class="hljs-string">'en'</span>, <span class="hljs-string">'Name in English'</span>);
</code></pre><p>Similarly, a specific language can be queried:</p>
<pre><code>Book::find($id)-&gt;getTranslation(<span class="hljs-string">'name'</span>, <span class="hljs-string">'en'</span>);
</code></pre><h3 id="migrations">Migrations</h3>
<p>Behind the scenes Translatable uses <code>JSON</code> columns in the database to store multiple versions in the same column:</p>
<pre><code>$table-&gt;json(<span class="hljs-string">'name'</span>);
</code></pre><h2 id="the-code">The code</h2>
<pre><code><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Traits</span>;

<span class="hljs-comment">/**
 * Trait allowing certain properties (columns) in Eloquent to be
 * translatable into different locales.
 * 
 * Default behavior is to return in the currently active locale.
 * 
 * Translatable properties are set in the Eloquent model with $translatable = [],
 * such as protected $translatable = ['name'];
 * 
 * Data stored as JSON (casted automatically), such as for name:
 * {"en": "Value in English", "da": "Value in Danish"}
 * 
 * $model-&gt;name will return either the value from en or da based on app()-&gt;getLocale().
 * Likewise, $model-&gt;name = 'New value' will set the value on the language based on app()-&gt;getLocale()
 * 
 * $model-&gt;getTranslation('name', 'da') will always return the Danish.
 * Likewise $model-&gt;setTranslation('name', 'da', 'New value in Danish')
 */</span>
<span class="hljs-keyword">trait</span> Translatable
{
    <span class="hljs-comment">/**
     * Override getCasts() to allow trait to set casts.
     *
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCasts</span>(<span class="hljs-params"></span>)
    </span>{
        $class = <span class="hljs-built_in">static</span>::class;

        <span class="hljs-keyword">foreach</span> (class_uses_recursive($class) <span class="hljs-keyword">as</span> $trait) {
            $method = <span class="hljs-string">'get'</span>. class_basename($trait) .<span class="hljs-string">'Casts'</span>;

            <span class="hljs-keyword">if</span> (method_exists($class, $method)) {
                <span class="hljs-keyword">$this</span>-&gt;casts = array_merge(
                    <span class="hljs-keyword">$this</span>-&gt;casts,
                    <span class="hljs-keyword">$this</span>-&gt;{$method}()
                );
            }
        }

        <span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::getCasts();
    }

    <span class="hljs-comment">/**
     * Get casts for the current trait.
     * 
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTranslatableCasts</span>(<span class="hljs-params"></span>)
    </span>{
        $casts = [];

        collect(<span class="hljs-keyword">$this</span>-&gt;translatable)-&gt;each(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">$item</span>) <span class="hljs-title">use</span>(<span class="hljs-params">&amp;$casts</span>) </span>{
            $casts[$item] = <span class="hljs-string">'json'</span>;
        });

        <span class="hljs-keyword">return</span> $casts;
    }

    <span class="hljs-comment">/**
     * Get translation if matches value in protected $translatable = [].
     * Otherwise calling parent __get($key)
     * 
     * <span class="hljs-doctag">@return</span> mixed
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__get</span>(<span class="hljs-params">$key</span>)
    </span>{
        <span class="hljs-keyword">if</span> (in_array($key, <span class="hljs-keyword">$this</span>-&gt;translatable)) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;getTranslation($key);
        }

        <span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::__get($key);
    }

    <span class="hljs-comment">/**
     * Set property value in current language if match found in protected $translatable = [].
     * Otherwise calling parent __set($key, $value)
     * 
     * <span class="hljs-doctag">@return</span> void
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__set</span>(<span class="hljs-params">$key, $value</span>)
    </span>{
        <span class="hljs-keyword">if</span> (in_array($key, <span class="hljs-keyword">$this</span>-&gt;translatable)) {
            <span class="hljs-keyword">if</span> (is_array($value)) {
                <span class="hljs-keyword">foreach</span>($value <span class="hljs-keyword">as</span> $locale =&gt; $val) {
                    <span class="hljs-keyword">$this</span>-&gt;setTranslation($key, $locale, $val);
                }
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">$this</span>-&gt;setTranslation($key, app()-&gt;getLocale(), $value);
            }
        }

        <span class="hljs-built_in">parent</span>::__set($key, $value);
    }

    <span class="hljs-comment">/**
     * Set translation in a given locale.
     * 
     * <span class="hljs-doctag">@param</span> String $key
     * <span class="hljs-doctag">@param</span> String $locale
     * <span class="hljs-doctag">@return</span> void
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setTranslation</span>(<span class="hljs-params"><span class="hljs-keyword">String</span> $key, <span class="hljs-keyword">String</span> $locale, $value</span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>(<span class="hljs-keyword">$this</span>-&gt;attributes[$key])) {
            $attribute = json_decode(<span class="hljs-keyword">$this</span>-&gt;attributes[$key]);
        }
        <span class="hljs-keyword">else</span> {
            $attribute = [];
        }

        $attributes[$locale] = $value;
        <span class="hljs-keyword">$this</span>-&gt;attributes[$key] = json_encode($attribute);
    }

    <span class="hljs-comment">/**
     * Get translation.
     * 
     * <span class="hljs-doctag">@param</span> string $key
     * <span class="hljs-doctag">@param</span> string $locale
     * <span class="hljs-doctag">@return</span> String
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTranslation</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $key, <span class="hljs-keyword">string</span> $locale = <span class="hljs-literal">null</span></span>): <span class="hljs-title">String</span>
    </span>{
        $locale = $locale ? $locale : app()-&gt;getLocale();

        <span class="hljs-keyword">return</span> json_decode(<span class="hljs-keyword">$this</span>-&gt;attributes[$key])-&gt;{$locale};
    }
}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Setup Postfix Relay Server with Microsoft Exchange Email]]></title><description><![CDATA[In this article, you will be guided on how to set up a  Postfix  relay SMTP-server and configure  Microsoft Exchange  to handle its mail queue.
In a later article, we will discuss how to set up the relay server as a docker container.
Prerequisites

A...]]></description><link>https://blog.emil.moe/setup-postfix-relay-server-with-microsoft-exchange-email</link><guid isPermaLink="true">https://blog.emil.moe/setup-postfix-relay-server-with-microsoft-exchange-email</guid><category><![CDATA[Laravel]]></category><category><![CDATA[Microsoft]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Tue, 26 Jan 2021 18:29:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685766287/2ADOKQlSo.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, you will be guided on how to set up a  <a target="_blank" href="http://www.postfix.org/">Postfix</a>  relay SMTP-server and configure  <a target="_blank" href="https://www.microsoft.com/en-us/microsoft-365/exchange/email">Microsoft Exchange</a>  to handle its mail queue.</p>
<p>In a later article, we will discuss how to set up the relay server as a docker container.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A server or access to create a new server (if you are using DigitalOcean: Droplet)</li>
<li>A  <a target="_blank" href="https://www.microsoft.com/en-us/microsoft-365/exchange/email">Microsoft Exchange</a>  account. It might work with a free Outlook 365 account, but this article will be based on Microsoft Exchange which costs about $4 per month.</li>
<li>Your Exchange email DNS has already been configured (You can send and receive emails on your domain)</li>
<li>Access to your applications DNS settings</li>
<li>Basic Linux and network skills (It’s mostly copy-paste)</li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>Sooner or later any web application must send out emails, however, ensuring the emails are not ending up in the recipient’s spam folder, server IP is blacklisted or other obstacles can either be expensive or time-consuming.</p>
<p>Some solutions offer an API to this where they handle the mail server part for a fee. A lot will have a free as well, however, you will then in both cases be sharing the outgoing IP with many other clients heavily increasing the risk of being assigned a blacklisted one.</p>
<p>The reality for CloudMonitor has been that the low-cost solutions couldn’t tackle our email problem and the higher-priced couldn’t be justified. Searching for other solutions we decided to set up a Postfix relay server that transports the queue to Microsoft Exchange. Some of the benefits of using Microsofts Exchange are:</p>
<ul>
<li>World-leading mail infrastructure</li>
<li>Never blacklisted</li>
<li>Cheap service</li>
</ul>
<p>Cons could be:</p>
<ul>
<li>No dashboard or metrics</li>
<li>No API</li>
</ul>
<p>These cons are fine for now as tracking can still be done with  <a target="_blank" href="https://matomo.org/">Matomo</a> .</p>
<h2 id="installing-postfix">Installing Postfix</h2>
<p>Since we are not going to handle questions about spam or sender validity, the setup of Postfix will be straight forward.</p>
<h3 id="mx-domain">MX Domain</h3>
<p>Before you start you should know the domain of your MX record. You can look up your domain at MX Toolbox to see which hostname is set for it:  <a target="_blank" href="https://mxtoolbox.com/SuperTool.aspx?action=mx:&amp;run=toolpage">https://mxtoolbox.com/SuperTool.aspx?action=mx:&amp;run=toolpage</a> </p>
<h3 id="configure-application-dns">Configure application DNS</h3>
<p>Open your Exchange dashboard at  <a target="_blank" href="https://admin.microsoft.com/">https://admin.microsoft.com/
</a> </p>
<p>Under domains click <strong>Add domain</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685157673/-BpDhn5An.png" alt="Add-Domain-1.png" /></p>
<p>Enter the domain name you want to add (The domain of your email sender address).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685204801/Cg-avxHBC.png" alt="Add-Domain-2.png" /></p>
<p>Select that you will verify your domain by <strong>TXT-record</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685228339/YYuyMIn3m.png" alt="Add-Domain-3.png" /></p>
<p>Copy the given details to your DNS settings</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685247822/3H6UwX6QZ.png" alt="Add-Domain-4.png" /></p>
<p>Here shown how it will look in DigitalOcean</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685272682/6V62KSaKn.png" alt="Add-Domain-5.png" /></p>
<p>After your DNS has been added, simply choose to continue on how you want to connect your domain</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685332297/nXGuY-jEh.png" alt="Add-Domain-6-1536x419.png" /></p>
<p>Now you will be given your DNS details under <strong>MX Records, CNAME Records</strong>, and <strong>TXT Records</strong>. Insert those in your DNS settings.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685354299/nICb6ulXz.png" alt="Add-Domain-7.png" /></p>
<p>Your domain is now added to Microsoft Exchange.</p>
<h3 id="install-postfix">Install Postfix</h3>
<p>On a newly created Ubuntu server install Postfix:</p>
<pre><code class="lang-shell">apt install postfix
postconf -e relayhost=&lt;MX-hostname&gt;
postconf -e smtp_use_tls=yes
postconf -e mynetworks=0.0.0.0/0
</code></pre>
<p>You must change <strong>MX-hostname</strong> with your own, but that’s it.</p>
<p>Make sure only you have access to the server, by, for example, restricting all incoming connections to your web server, or at minimum port 25.</p>
<h2 id="configure-microsoft-exchange">Configure Microsoft Exchange</h2>
<p>This step requires you to have a paid Exchange plan from Microsoft. It might work with the free Outlook mailbox too, but we haven’t tested it.</p>
<p>First go to your Exchange control panel:  <a target="_blank" href="https://admin.exchange.microsoft.com/">https://admin.exchange.microsoft.com/</a> </p>
<p>Find <strong>Connectors</strong> and click <strong>Add a connector</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685398987/LmgArnh3S.png" alt="Relay-1.png" /></p>
<p>Choose <strong>Your organization’s email server</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685412762/kuEKZjck0.png" alt="Relay-2.png" /></p>
<p>Name your integration, we choose the name of the application</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685424702/1x7xzSRgz.png" alt="Relay-3.png" /></p>
<p>Choose <strong>Verify by IP</strong> and enter the IP of your relay server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685436145/vs4w5XBBS.png" alt="Add-Domain-5.png" /></p>
<p>Click <strong>Create</strong>. Now the Exchange setup is done.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1611685449709/3ntfZASqD.png" alt="Relay-5.png" /></p>
<hr />
<p>This post was originally posted on  <a target="_blank" href="https://cloudmonitor.dk/setup-postfix-relay-server-with-microsoft-exchange-email/">https://cloudmonitor.dk/setup-postfix-relay-server-with-microsoft-exchange-email/</a> </p>
]]></content:encoded></item><item><title><![CDATA[Laravel: Unique Validation with Another Column]]></title><description><![CDATA[Sometimes you need to ensure a record is unique, but only if another has a certain value. Does this sound strange? Let me give you an example:
The SaaS Problem
You are building a SaaS and each customer can add users, but users can only be added once,...]]></description><link>https://blog.emil.moe/laravel-unique-validation-with-another-column</link><guid isPermaLink="true">https://blog.emil.moe/laravel-unique-validation-with-another-column</guid><category><![CDATA[Laravel]]></category><category><![CDATA[Validation]]></category><category><![CDATA[PHP7]]></category><category><![CDATA[eloquent]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Fri, 04 Dec 2020 20:27:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1607113590409/5wHe6a1cS.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sometimes you need to ensure a record is unique, but only if another has a certain value. Does this sound strange? Let me give you an example:</p>
<h4 id="the-saas-problem">The SaaS Problem</h4>
<p><em>You are building a SaaS and each customer can add users, but users can only be added once, hence we want unique emails. However, the email must not be unique for all customers therefore we have to make this rule: For each unique customer id, all emails must be unique.</em></p>
<h4 id="use-the-rule-class">Use the Rule-class</h4>
<p>The problem is that you can no longer use the basic <code>unique:email</code> rule anymore as this would block other customers from inviting that user.</p>
<p>The solution is to implement Laravels <code>Rule</code>-class.</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'email'</span> =&gt; Rule::unique(<span class="hljs-string">'users'</span>, <span class="hljs-string">'email'</span>)-&gt;where(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">$query</span>) </span>{
        $query-&gt;where(<span class="hljs-string">'customer_id'</span>, User::findOrFail(<span class="hljs-keyword">$this</span>-&gt;user)-&gt;customer_id)
            -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'&lt;&gt;'</span>, <span class="hljs-keyword">$this</span>-&gt;user);
    }),
];
</code></pre>
<h4 id="syntax-explained">Syntax Explained</h4>
<p>The concept here, as said, is to ensure the email is only unique across that customer's users. Let's look at the syntax:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-comment">// Tell Laravel which column it looks to be unique. Typically the same as on the left side</span>
    <span class="hljs-string">'email'</span> =&gt; Rule::unique($table, $column)-&gt;where(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">$query</span>) </span>{
        <span class="hljs-comment">// Add the extra criteria column, filtering the lookup to only those belonging to the same customer as the current user is</span>
        $query-&gt;where($filterColumnCriteria, User::findOrFail(<span class="hljs-keyword">$this</span>-&gt;user)-&gt;{$filterColumnCriteria})
            <span class="hljs-comment">// Last, skip the user being update avoiding error because it finds itself</span>
            -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'&lt;&gt;'</span>, <span class="hljs-keyword">$this</span>-&gt;user);
    }),
];
</code></pre>
<h4 id="implementation">Implementation</h4>
<p>The round up in a sample Request file:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">FormRequest</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FormRequest</span>
</span>{
    <span class="hljs-comment">/**
     * Determine if the user is authorized to make this request.
     *
     * <span class="hljs-doctag">@return</span> bool
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">authorize</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-comment">/**
     * Get the validation rules that apply to the request.
     *
     * <span class="hljs-doctag">@return</span> array
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rules</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> [
            <span class="hljs-string">'email'</span> =&gt; Rule::unique(<span class="hljs-string">'users'</span>, <span class="hljs-string">'email'</span>)-&gt;where(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">$query</span>) </span>{
                $query-&gt;where(<span class="hljs-string">'customer_id'</span>, User::findOrFail(<span class="hljs-keyword">$this</span>-&gt;user)-&gt;customer_id)
                    -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'&lt;&gt;'</span>, <span class="hljs-keyword">$this</span>-&gt;user);
            }),
        ];
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[How to Disable Links With Only CSS]]></title><description><![CDATA[Agreed, this is a bit of a hack.
To keep things streamlined with WordPress, I had to implement this hack the other day, where I wanted to disable tag links on just 1 page.
Instead of fiddling with a new plugin, I found it easier, in this case, to dis...]]></description><link>https://blog.emil.moe/how-to-disable-links-with-only-css</link><guid isPermaLink="true">https://blog.emil.moe/how-to-disable-links-with-only-css</guid><category><![CDATA[CSS]]></category><category><![CDATA[CSS3]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Wed, 18 Nov 2020 21:57:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605736637172/NJcXTDUTF.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Agreed, this is a bit of a hack.</p>
<p>To keep things streamlined with WordPress, I had to implement this hack the other day, where I wanted to disable tag links on just 1 page.</p>
<p>Instead of fiddling with a new plugin, I found it easier, in this case, to disable the link in the template engine with pure CSS.</p>
<p>The magic to remove the link feature lies in <code>pointer-events</code> while the rest ensures it doesn't look like one.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">a</span><span class="hljs-selector-class">.disabled</span> {
  <span class="hljs-attribute">pointer-events</span>: none;      
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">cursor</span>: default;  
  <span class="hljs-attribute">text-decoration</span>: none;
}
</code></pre>
<p>The semantics might be up for discussion, but the performance is possibly better than using Javascript.</p>
<p>This approach is available to all major browsers:  <a target="_blank" href="https://caniuse.com/pointer-events">CSS pointer-events (for HTML)</a> </p>
]]></content:encoded></item><item><title><![CDATA[Why I Believe No Code Tools Are OK – And When]]></title><description><![CDATA[In the field of developers, I have often heard that GUI tools or WYSIWYG is the bad path to take. Honestly, I tend to be like this too.
The reasons are such as the quality of your product won't be as good, the code might even be a mess. You might als...]]></description><link>https://blog.emil.moe/why-i-believe-no-code-tools-are-ok-and-when</link><guid isPermaLink="true">https://blog.emil.moe/why-i-believe-no-code-tools-are-ok-and-when</guid><category><![CDATA[Hashnode]]></category><category><![CDATA[General Advice]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[motivation]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Mon, 17 Aug 2020 15:45:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1597679031787/RU45kKGvT.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the field of developers, I have often heard that GUI tools or WYSIWYG is the bad path to take. Honestly, I tend to be like this too.</p>
<p>The reasons are such as the quality of your product won't be as good, the code might even be a mess. You might also have heard other reasons like lack of flexibility and hard to debug.  </p>
<h2 id="why-i-chose-to-try-a-gui-tool">Why I Chose To Try a GUI Tool</h2>
<p>I support all these reasons and I still believe they are true to some extend. However, as I have moved forward in my life and career along the road I have also learned that the real world is unfortunately not only a matter of quality. Sometimes we need to prototype fast, to test if an idea has its right to exist or it was just good on the planning board.</p>
<p>This doesn't mean quantity over quality, but it means that the old concept of re-factoring is indeed very valid here. You should prototype fast, GUI tools are perfectly fine here, as this is not the time in your project where your fiddle with 1 or 2px to the left, but rather focus on first releases.</p>
<h2 id="my-experience-using-a-ui-tool">My Experience Using A UI Tool</h2>
<p>As your product grows, you will most likely realise that the GUI tool had its limits. This is why refactoring should always be in mind. Don't build your prototype on a closed platform where all your data might be stuck behind another company's proprietary wall.</p>
<p>Instead build with a tool that allows you for fast progress. Later on, if you have success, you build your custom solution and migrate the data. By this time you are also aware of what you need to do custom made.</p>
<h3 id="blogging-example">Blogging Example</h3>
<p>An example could be blogging. Hashnode is a great tool and platform to get started fast. When you have (for example) 100,000 daily visitors from not within the Hashnode community, you might start considering if you should build your own platform.</p>
<hr />
<p><em>I chose to write my thoughts, after I saw the similar topic here:  <a href="https://redlotusdesignz.hashnode.dev/why-its-perfectly-ok-to-use-no-code-tools-as-a-developer-ckduo4ftd0016aqs17j0r54ij?guid=c71274c3-89cd-480a-856c-a6a9c43899c6&amp;deviceId=a05033d4-523a-428a-839a-78a4f0dd17bd">Why it’s perfectly OK to use no-code tools as a developer</a></em></p>
]]></content:encoded></item><item><title><![CDATA[Schedule Tasks in Laravel and Debug Them]]></title><description><![CDATA[Automated tasks has existed on many platforms for a long time. The purpose can be many, but in web applications it's often to optimise speed for the user on heavy duties by caching results.
It can also be used  to perform  various maintenance tasks t...]]></description><link>https://blog.emil.moe/schedule-tasks-in-laravel-and-debug-them</link><guid isPermaLink="true">https://blog.emil.moe/schedule-tasks-in-laravel-and-debug-them</guid><category><![CDATA[Laravel]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Hashnode]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Fri, 14 Aug 2020 10:15:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1597399695797/pvuMb87cv.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Automated tasks has existed on many platforms for a long time. The purpose can be many, but in web applications it's often to optimise speed for the user on heavy duties by caching results.</p>
<p>It can also be used  to perform  various maintenance tasks to easen administration. You can think of Windows Update that runs in the background.</p>
<h3 id="cron">Cron</h3>
<p>If you, like most people, are running Laravel on Linux, ie Ubuntu, it's important to understand the basics of cron in order to understand how task scheduling works.</p>
<p>A brief summary of the terminology</p>
<h4 id="cron-job">Cron job</h4>
<p>A scheduled task in cron is called a cron job.</p>
<h4 id="crontab">Crontab</h4>
<p>All your cron jobs are stored in a table this is shortened to crontab (cron table).</p>
<p>On Linux you can edit the table with <code>cron -e</code> or list the entries with <code>cron -l</code>.</p>
<h4 id="syntax-and-interval">Syntax and interval</h4>
<p>The syntax for a cron job in the crontab is:</p>
<p><code>Interval</code> <code>Command</code></p>
<p>The interval is defined by 5 numbers:</p>
<ul>
<li>Minute</li>
<li>Hour</li>
<li>Day of month</li>
<li>Month</li>
<li>Day of week</li>
</ul>
<p>Asterix (*) is used to define an any paramater. If you want to run a command every minute it's simply: <code>* * * * * command</code></p>
<p>I will not go further into the intervals, but you can generate your own at sites like https://crontab-generator.org/</p>
<h3 id="laravel-task-scheduling">Laravel task scheduling</h3>
<p>It's important to understand the concepts of cron as Laravels task scheduling is tightly coupled to it. For example look at the first method described in the  <a target="_blank" href="https://laravel.com/docs/master/scheduling#schedule-frequency-options">documentation</a>:</p>
<pre><code class="lang-php">-&gt;cron(<span class="hljs-string">'* * * * *'</span>);
</code></pre>
<p>The rest of the methods are basically syntactic sugar to help you maintain more a readable code.</p>
<p>Laravels task scheduling is in fact not scheduling in itself. It's constructed so you run a cronjob every minute against Laravel which then checks its internal table to see if any commands are due to execution. This is important to know, if you want to investigate why your scheduled tasks are not running, it might be the cronjob is missing or not working.</p>
<h4 id="laravel-cronjob">Laravel cronjob</h4>
<p>The cronjob for Laravel is (<code>crontab -e</code>):</p>
<pre><code class="lang-shell">* * * * * cd /path-to-your-project &amp;&amp; php artisan schedule:run &gt;&gt; /dev/null 2&gt;&amp;1
</code></pre>
<h4 id="cronjob-permissions">Cronjob permissions</h4>
<p>You  should  add this cronjob in <code>www-data</code> (or what other name your web user has) in order to retain proper permission on files.</p>
<h3 id="use-cases">Use cases</h3>
<h4 id="caching">Caching</h4>
<p>As mentioned earlier caching is a situation where you can benefit from background tasks. An example from my own experience is where we have a pivot table. A pivot table is a useful tool if your clients needs to lookup data in many different ways with filters, customised output and so on. However, since the data come from across the whole application and the tables have several thousands of records, it's not a few seconds job to collect the data.</p>
<p>Instead we have set up task scheduling to cache this whole output every 15th minute (<code>-&gt;everyFifteenMinutes();</code>) so with a slight delay the users can load the pivot table in a few seconds instead of minutes.  </p>
<h4 id="maintenance">Maintenance</h4>
<p>Another useful situation is when you want to perform maintenance jobs, but don't want to have to click a button every day to execute the maintenance script.</p>
<p>You might have implemented GDPR and added a feature where users have the right to be forgotten. You might also leave a  door open, so that they can cancel the process within 14 days. This means when they request deletion you mark them with a timestamp <code>deletion_request_at</code>, after 14 days, if not nulled,  they will be removed.</p>
<p>You could set a task to run every midnight (<code>-&gt;daily();</code>) to check if any such users has passed 14 days. </p>
<h3 id="debug-task-scheduling">Debug task scheduling</h3>
<p>A common issue with scheduled tasks, since they are performed in the background, is that it can be difficult to monitor if they are running at all and when they are failing it can be even more difficult to understand what went wrong.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1597398925077/ryEiXBQwv.png" alt="image.png" /></p>
<p>For that reason I created a Task feature in <a target="_blank" href="https://cloudmonitor.dk">CloudMonitor</a> that will not just monitor if your scheduled tasks are running as expected, but will also show you the output so you know what exactly went wrong.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1597398877178/oykPhi3vU.png" alt="image.png" /></p>
<p>A log history allows you further to learn if it's the same error that is happening every time or they differ. It could be sometimes a remote service don't respond and other times your memory is exhausted, etc.</p>
<hr />
<p><em><a target="_blank" href="https://cloudmonitor.dk">CloudMonitor.dk</a>  – Free Laravel Maintenance- and Bug Management</em></p>
]]></content:encoded></item><item><title><![CDATA[Introduction to secured client server API]]></title><description><![CDATA[Preface
In modern software architecture it's often necessary to communicate between applications a common way to do this is by using an API. APIs (Application Programming Interface) are interfaces optimised for applications rather than humans as thei...]]></description><link>https://blog.emil.moe/introduction-to-secured-client-server-api</link><guid isPermaLink="true">https://blog.emil.moe/introduction-to-secured-client-server-api</guid><category><![CDATA[Laravel]]></category><category><![CDATA[APIs]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[Hashnode]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Wed, 12 Aug 2020 09:21:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1597234841052/QPZeU0rJO.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="preface">Preface</h2>
<p>In modern software architecture it's often necessary to communicate between applications a common way to do this is by using an API. APIs (<em>Application Programming Interface</em>) are interfaces optimised for applications rather than humans as their purpose is to transfer data from either backend to frontend, for example PHP to JavaScript or from one application to another.</p>
<p>This post I will focus on the second case, between 2 applications. When doing so, you transfer data across the internet and must therefore, for a numerous reasons, retain more focus on security, such as man-in-the-middle-attacks, spoofing and so on. In other words you must make sure both server and client can identify and validate each other and data between is encrypted.</p>
<h2 id="implementation">Implementation</h2>
<p>The idea is that you send some unencrypted data as headers, this could be:</p>
<ul>
<li>Timestamp</li>
<li>Version</li>
<li>Token</li>
<li>Signature</li>
</ul>
<p>Along the headers the body contains an encrypted message that needs all or some of the headers to be decrypted with a secret.</p>
<h4 id="timestamp">Timestamp</h4>
<p>The time the message was sent, this can be used to monitor transfer delay but also as a parameter to the signature.</p>
<h4 id="version">Version</h4>
<p>Tells the server which version the client is on, you can both use this data to know how to handle the incoming request on the server, but also to know when a version is not used anymore or which clients to upgrade.</p>
<h4 id="token">Token</h4>
<p>The token, also often known as key, tells the server which client or configuration, is the author to lookup for the secret stored on the server. It could be as simple as an <code>user id</code>.</p>
<h4 id="signature">Signature</h4>
<p>The signature is generated with a set set of the parameters to create a unique key that the server can use to validate the authenticity of the sender.</p>
<h3 id="secret">Secret</h3>
<p>The secret is very important here, it's the key that should only be known by host and client and never shared and also easy to regenerate in case it was mistakenly shared. </p>
<h2 id="implementation-in-laravel">Implementation in Laravel</h2>
<h4 id="signature">Signature</h4>
<p>First the client should generate the signature, let's assume we have these variables we are going to work with:</p>
<pre><code class="lang-php">$timestamp = time();
$version = <span class="hljs-string">'1.0.0'</span>;
$token = <span class="hljs-string">'my_key'</span>;
$secret = <span class="hljs-string">'my_secret'</span>;
</code></pre>
<p>To validate the signature, both server and client must have the same method in order to parse the parameters to the same result. A method to create a signature using <code>hash_hmac</code> could be:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createSignature</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $timestamp, <span class="hljs-keyword">string</span> $secret</span>): <span class="hljs-title">string</span>
</span>{
    <span class="hljs-keyword">return</span> hash_hmac(<span class="hljs-string">'sha256'</span>, $timestamp . $secret);
}
</code></pre>
<p>The advantage here using a <code>hash</code> algorithm is that it's not designed to be decrypted again. We are not interested in the actual content of the plain text, just if we can match the string on both servers.</p>
<h4 id="the-message">The message</h4>
<p>Now we have the following variables:</p>
<pre><code class="lang-php">$timestamp = time();
$version = <span class="hljs-string">'1.0.0'</span>;
$token = <span class="hljs-string">'my_key'</span>;
$secret = <span class="hljs-string">'my_secret'</span>;
$signature = createSignature($timestamp, $secret);
</code></pre>
<p>Unlike the signature, we want to be able to decrypt the content of the message. Since this guide is based on Laravel I benefit from the build in <code>Encryption</code> class <code>Illuminate\Encryption\Encrypter</code>.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Encryption</span>\<span class="hljs-title">Encrypter</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encrypt</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $secret, <span class="hljs-keyword">string</span> $message</span>): <span class="hljs-title">string</span>
</span>{
    $encrypter = <span class="hljs-keyword">new</span> Encrypter($secret, <span class="hljs-string">'AES-128-CBC'</span>);

    <span class="hljs-keyword">return</span> $encrypter-&gt;encrypt($message);
}
</code></pre>
<p>Here we use the secret in order to encrypt the content of our message, if you have an array you could use <code>json_encode</code>/<code>json_decode</code>.</p>
<h4 id="the-secret">The secret</h4>
<p>This illustrates why it's important to keep the secret secured as it's the <em>magic key</em> to break into your encrypted system.</p>
<h3 id="sending-data">Sending data</h3>
<p>When using Laravel I will recommend using <code>Guzzle</code> as it's straight forward easy to work with. Here's how you can do it:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">send</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $message, <span class="hljs-keyword">string</span> $key, <span class="hljs-keyword">string</span> $secret, <span class="hljs-keyword">string</span> $url</span>) </span>{
    $client = <span class="hljs-keyword">new</span> Client();
    $response = <span class="hljs-literal">null</span>;
    $timestamp = time();
    $version = <span class="hljs-string">'1.0.0'</span>;

    <span class="hljs-keyword">try</span> {
        $response = $client-&gt;request(
            <span class="hljs-string">'POST'</span>,
            $url,
            [
                <span class="hljs-string">'headers'</span> =&gt; [
                    <span class="hljs-string">'timestamp'</span> =&gt; $timestamp,
                    <span class="hljs-string">'version'</span> =&gt; $version,
                    <span class="hljs-string">'token'</span> =&gt; $key,
                    <span class="hljs-string">'signature'</span> =&gt; createSignature($timestamp, $secret),
                ],
                <span class="hljs-string">'form_params'</span> =&gt; [
                    <span class="hljs-string">'data'</span> =&gt;encrypt($secret, $message)
                ]
            ]
        );
    } <span class="hljs-keyword">catch</span> (<span class="hljs-built_in">Exception</span> $e) {
        <span class="hljs-keyword">throw</span> $e;
    }

    <span class="hljs-keyword">return</span> $response;
}
</code></pre>
<hr />
<p><em><a target="_blank" href="https://cloudmonitor.dk">CloudMonitor.dk</a>  – Free Laravel Maintenance- and Bug Management</em></p>
]]></content:encoded></item><item><title><![CDATA[How I created CloudMonitor.dk]]></title><description><![CDATA[In my previous article Bug Management for Laravel I explained what CloudMonitor is.
In this article I want to let you in on how I created it in terms of technologies and techniques. Later I will dig into some of the topics mentioned here to cover the...]]></description><link>https://blog.emil.moe/how-i-created-cloudmonitordk</link><guid isPermaLink="true">https://blog.emil.moe/how-i-created-cloudmonitordk</guid><category><![CDATA[Laravel]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Tue, 11 Aug 2020 08:04:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1597133974747/mwVchKAGY.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my previous article <a target='_blank' rel='noopener'  href="https://blog.emilmoe.com/bug-management-for-laravel-ckdhg3kal000pyvs10ag3hwkm">Bug Management for Laravel</a> I explained what CloudMonitor is.
In this article I want to let you in on how I created it in terms of technologies and techniques. Later I will dig into some of the topics mentioned here to cover theme in deep. Feel free to request one.</p>
<h2 id="the-tech-stack">The tech stack</h2>
<p>The stack I have used to create this project is:</p>
<ul>
<li>DigitalOcean</li>
<li>Laravel 7</li>
<li>Vue 2 </li>
<li>MySQL</li>
<li>Bootstrap</li>
</ul>
<p>These are all applications, platforms and frameworks I have been using extensively for years, thus I have a vast experience. However there was obstacles on the way and this was also intended. I wanted to challenge myself and to both learn new techniques but also show off what I can do.</p>
<h2 id="digitalocean">DigitalOcean</h2>
<p>Is used to tackle all the infrastructure, from a Ubuntu server (Droplet) with Apache2, firewall, nameservers, cloud backup (Spaces) and managed database (MySQL). The great thing about thing about DigitalOcean is that  it&#39;s very  cost effective to get started as a startup. The only peak in my setup is the managed database, but that could easily be hosted on another Droplet (self-managed) or even the same Droplet as your Apache2/webhost, though the last option is not recommended for production.</p>
<p>For instance if you want to setup similar to mine with webserver, database and backup:</p>
<table>
<thead>
<tr>
<td>Instance</td><td>Price per month</td></tr>
</thead>
<tbody>
<tr>
<td>Droplet</td><td>$5</td></tr>
<tr>
<td>Spaces</td><td>$5</td></tr>
<tr>
<td>Managed Database</td><td>$30</td></tr>
<tr>
<td>Per month</td><td>$40</td></tr>
</tbody>
</table>
<p>What I love about DigitalOcean is their straight forward, and cheap, prices, where AWS has a lot of running costs that can be hard to calculate. DigitalOcean for instance offers free nameservers, where that has a small price for AWS.</p>
<p>If you want to  try DigitalOcean you can use my link:  <a target='_blank' rel='noopener'  href="https://m.do.co/c/656a65595ddb">DigitalOcean</a> - it&#39;s an offer for both us:</p>
<blockquote>
<p>Everyone you refer gets $100 in credit over 60 days. Once they’ve spent $25 with us, you&#39;ll get $25.</p>
</blockquote>
<h2 id="laravel-vue-and-bootstrap">Laravel, Vue and Bootstrap</h2>
<p>As this is a tool to help Laravel, it only made sense, that the tool itself  is  written in Laravel. The idea of the package for you install in your application to communicate with the server is to work out of the box, with a very minimum of configuration.</p>
<p>Where Laravel is the backend serverside language, written in PHP, serving all the logic in the background, Vue is a Javascript framework and therefor handles all the dynamic content in the interface.</p>
<p>The styling and layout is handled by  <a target='_blank' rel='noopener'  href="https://getbootstrap.com/">Bootstrap</a> which allows for a streamlined standardised layout. A great tool I have found along the way is  <a target='_blank' rel='noopener'  href="https://bootstrap.build/">bootstrap.build</a> where you can style all its components online and refer to  a CDN, before you download the modified files, this better ensures for strictly following Bootstraps guidelines while customising the look.</p>
<h2 id="underlying-techniques">Underlying techniques</h2>
<p>There are many underlying techniques I have been tackling in this project, so I will just  cover a few here.</p>
<h3 id="package-development">Package development</h3>
<h4 id="key-secret">Key / secret</h4>
<p>As the platforms purpose is reporting errors on other applications, I naturally had to create a client package that can report these incidents. So it was not just one, the main, application, but two. This would not work without some sort of communication between them and since fragments of code is to be sent in order for better debugging I felt it was needed to encrypt this communication.
I ended up using a key/secret approach. The idea is  the  whole  message on the client is  encrypted using the secret, which the server also stores and is not transmitted at all. The key is sent along the encrypted message to tell the server which key to lookup to find the unlock secret.
The server keeps a throttle limit so too many failed requests will result in a block to avoid brute force.</p>
<h4 id="testing">Testing</h4>
<p>One of the biggest issues I had for a long time before I found a solution was to test the client package. One thing is to develop a client for your main application, but often the errors happens when something goes wrong with the communication to the server. I implemented a few steps to tackle that:</p>
<ul>
<li><strong>Versioning</strong>, ensuring that client and server a tracking on the  same versions, and if breaking changes must be added, the server can handle requests based on the clients version.</li>
<li><strong>Communication</strong>, I added methods for sending test incidents through the system, so I could force an error to see everything is working before releasing either the server or the client update including debugging information both back to the client and stored on the server.</li>
</ul>
<hr>
<p> <a target='_blank' rel='noopener'  href="https://cloudmonitor.dk">CloudMonitor.dk</a> </p>
]]></content:encoded></item><item><title><![CDATA[Bug Management for  Laravel]]></title><description><![CDATA[You probably came to here because you are a Laravel developer and  maybe because you are  searching for a bug  management solution specific for Laravel.
I have been working professionally fulltime with Laravel for 6 years now and wanted to create a p...]]></description><link>https://blog.emil.moe/bug-management-for-laravel</link><guid isPermaLink="true">https://blog.emil.moe/bug-management-for-laravel</guid><category><![CDATA[Laravel]]></category><category><![CDATA[Bugs and Errors]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Wed, 05 Aug 2020 14:06:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1596636568992/DKb4VlUeE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You probably came to here because you are a Laravel developer and  maybe because you are  searching for a bug  management solution specific for Laravel.</p>
<p>I have been working professionally fulltime with Laravel for 6 years now and wanted to create a platform that gives me better insights to running applications. I don&#39;t believe these utilities should come as a part of an application but rather an external tool that by  time can generate its own useful data for further development.</p>
<p>Therefor I used my knowledge and started on  a project I call   <a target='_blank' rel='noopener'  href="https://cloudmonitor.dk">CloudMonitor</a>. As for now I have implemented an outline of what I feel is core essential features for its purpose:</p>
<ul>
<li>Bug tracking</li>
<li>Task </li>
<li>Backup</li>
<li>Uptime</li>
</ul>
<p>All this I have chosen to provide for free. The project is still, as many projects, in development and you have a  great opportunity take part in the future as the roadmap is flexible enough for inputs.</p>
<h3 id="bug-tracking">Bug tracking</h3>
<p>This works as any bug tracker you know (Sentry, Bugsnag). You can see a snippet of what is going wrong, a stack trace and query log. Also a collection of events to see  how  often  it occurs and maybe even proactively contact clients to follow up on an issue before they face it too many times with frustrations.</p>
<h3 id="tasks">Tasks</h3>
<p>Laravel features scheduled jobs or tasks, which is a very useful tool, however it lacks of good insights and this is what&#39;s shown here. Quickly see if the task failed, missing or succeeded but also watch the output in the terminal which is especially useful when it fails to know what failed – it might not always result in an exception.</p>
<h3 id="backup">Backup</h3>
<p>For now it&#39;s build upon Spatie Backup and utilises events from there to track if backups are running and healthy, but also if the cleanup is running.</p>
<h3 id="uptime">Uptime</h3>
<p>Checking every 10 minutes to  check if the domain is still accessible and if HTTPS that the certificate is still valid.</p>
<h2 id="overall">Overall</h2>
<p>Everything is done with logs in mind, so you can build statistics on your applications performance and as an extra feature you can add  what&#39;s called &#39;Installations&#39;, which is collecting data from 2 or more servers running the same software. This  is useful if you setup up the same codebase for 2 clients on different servers, but it&#39;s not a SaaS, since a bug in one installation would also be a bug in the other and collection of events might be useful across both.</p>
<hr>
<p> <a target='_blank' rel='noopener'  href="https://cloudmonitor.dk">CloudMonitor.dk</a> </p>
]]></content:encoded></item><item><title><![CDATA[Starters guide to Laravel templates, with Bootstrap]]></title><description><![CDATA[In this guide I will be guiding you through the basic concepts of defining a base layout in Laravel using Blade templates. I will share with you how I after years of working with Laravel choose to structure my layouts. We will be creating our example...]]></description><link>https://blog.emil.moe/starters-guide-to-laravel-templates-with-bootstrap</link><guid isPermaLink="true">https://blog.emil.moe/starters-guide-to-laravel-templates-with-bootstrap</guid><category><![CDATA[Laravel]]></category><category><![CDATA[Bootstrap]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Thu, 30 Jul 2020 08:28:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1595750596403/Mh4LDTI8r.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this guide I will be guiding you through the basic concepts of defining a base layout in Laravel using Blade templates. I will share with you how I after years of working with Laravel choose to structure my layouts. We will be creating our example using Bootstrap, so you have something to continue with after, if you prefer other frameworks such as Tailwind, you should be able to do so, but already knowing the CSS framework would be beneficial then.</p>
<p>I will cover the basic use of Laravel Blade with Bootstrap for a signed in user, you should get the idea afterwards if you wish to create a layout for the landing page as well.</p>
<p>This article is written with Laravel 7 in mind.</p>
<h2 id="setup">Setup</h2>
<p>After you setup your initial Laravel you should use Laravel/UI to install Bootstrap:</p>
<ol>
<li>Install Laravel/UI: <code>composer require laravel/ui</code></li>
<li>Prepare Bootstrap: <code>php artisan ui bootstrap</code></li>
<li>Install assets: <code>npm install</code></li>
</ol>
<h2 id="structure-and-core-layout">Structure and core layout</h2>
<h3 id="worth-noting-about-laravel-blade">Worth noting about Laravel Blade</h3>
<h4 id="section-names">Section names</h4>
<p>You  can only use a section name once in a structure, this means you are going to have a conflict if you 2 templates uses the same section name:</p>
<pre><code class="lang-html">// Template 1
@yield('content')

// Template2
@extends('Template1')
@section('content')
  @yield('content')
@endsection
</code></pre>
<p><strong><em> This is prone to errors ! </em></strong></p>
<h4 id="ending-sections">Ending sections</h4>
<p>A section can be ended by either <code>@stop</code> or <code>@endsection</code>. Which you choose is up to you, but in my opinion <code>@endsection</code> gives you a more readable template.</p>
<h3 id="structure">Structure</h3>
<p>I like to keep my structure as closely related to the one suggested from Laravel, this ensures it's easier for me in the future to go back and modify the code, but also easier for participants to understand my code. Therefor all Blade templates are stored in <code>/resources/views</code>. </p>
<p>Layout  specific templates will be put inside the subdirectory <code>layout</code>. Within that I like to keep a subfolder called <code>parts</code>, this contains menu, footer, header and other commonly used parts that should be accessible through the various layout templates.</p>
<h4 id="base">Base</h4>
<p>Within <code>layout</code> create a new file called <code>base.blade.php</code>. This is the base template for all templates – but only extended by layout templates.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span> <span class="hljs-attr">profile</span>=<span class="hljs-string">"http://www.w3.org/2005/10/profile"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My site<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"app-url"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"{{ url('/') }}"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url(mix('css/app.css')) }}"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        @yield('page')
        <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{{ url(mix('js/app.js')) }}"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h4 id="blank">Blank</h4>
<p>Next create a file in <code>layout</code> called <code>blank.blade.php</code>, this will be the most basic layout. It may seem redundant not just to extend base, but this way we can keep a streamlined architecture and sometimes you might want to force ie a footer to even the minimal layout.</p>
<pre><code class="lang-html">@extends('layout.base')

@section('page')
    @yield('content')
@endsection
</code></pre>
<p>Also, we ensured the section you later refer to is always <code>content</code>.</p>
<h4 id="app">App</h4>
<p>For the signed in user the there should be a layout that represents the app view. This will include:</p>
<ul>
<li>Navigation</li>
<li>Header</li>
<li>App content area</li>
</ul>
<pre><code class="lang-html">@extends('layout.base')

@section('page')
    @include('layout.parts.navbar')

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row h-100 mt-3"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col"</span>&gt;</span>
                @yield('content')
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
@endsection
</code></pre>
<h4 id="include-and-yield"><code>@include</code> and <code>@yield</code></h4>
<p>We have worked with 2 different ways to work with external template files. The difference is in logic, or direction you might say, they are injected. <code>@include</code> is when the template working in knows which external template to inject, such as navigation. This is useful when you have parts of templates that are used in many templates but don't want to rewrite it.</p>
<p><code>@yield</code> is when the template working in, doesn't know the content, this should be more seen as a variable placeholder for child templates to inject content into the parent. (<code>@section</code>).</p>
<p><em>Not covered here, but useful to add is a global handler for alert and info messages.</em></p>
<h5 id="navbar">Navbar</h5>
<p>For our navbar this should be stored in <code>layouts.parts.navbar</code>. As the navbar isn't going to have any sub-elements I will just use the default from Bootstrap:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar navbar-expand-lg navbar-light bg-light"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Navbar<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">data-toggle</span>=<span class="hljs-string">"collapse"</span> <span class="hljs-attr">data-target</span>=<span class="hljs-string">"#navbarNavAltMarkup"</span> <span class="hljs-attr">aria-controls</span>=<span class="hljs-string">"navbarNavAltMarkup"</span> <span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Toggle navigation"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler-icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"collapse navbar-collapse"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"navbarNavAltMarkup"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-nav"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item nav-link active"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Home <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>(current)<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item nav-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Features<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre>
<h3 id="using-the-layout">Using the layout</h3>
<h4 id="extends"><code>@extends</code></h4>
<p>Now building our content page we can benefit from the layout templates. <code>@extends</code> allows to  inherit a parent template sort of how <code>extends</code> works for a class in PHP. I  like to put <code>@extends</code> at the top of my blade files.</p>
<p>Create the file <code>/resources/views/content.blade.php</code> with the following content:</p>
<pre><code class="lang-html">@extends('layouts.app')
</code></pre>
<h4 id="section"><code>@section</code></h4>
<p>Remember we used <code>@yield</code> in the layouts file?  This is referred to as sections in child templates. This means we create a section in the child, which is injected at the yield point in the parent template.</p>
<p><strong>You should avoid ending up with 2 names that are the same, unless it's unlikely the templates are used together such as for the different layouts</strong></p>
<p>Make your content site look like this:</p>
<pre><code class="lang-html">@extends('layouts.app')

@section('content')
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col"</span>&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
                 <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-body"</span>&gt;</span>
                     My content goes here, both plain text, HTML and Blade syntax.
                 <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
             <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
@endsection
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Laravel: Seed Users table and send password reset link]]></title><description><![CDATA[A useful feature for setting up your application fast is using the seeders, especially for testing and developing it's great to have data to work on with just 1 command.
A problem for me though was that if  I seed users with a password it would mean ...]]></description><link>https://blog.emil.moe/laravel-seed-users-table-and-send-password-reset-link</link><guid isPermaLink="true">https://blog.emil.moe/laravel-seed-users-table-and-send-password-reset-link</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Tue, 12 Nov 2019 10:34:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1585125676735/at_-q1gDb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A useful feature for setting up your application fast is using the seeders, especially for testing and developing it's great to have data to work on with just 1 command.</p>
<p>A problem for me though was that if  I seed users with a password it would mean I either have to store the passwords in the <code>.env</code>-file or commit them to git.</p>
<p>So instead I researched and found a way to generate a password reset token and send the notifications, so now when I seed users will receive a link to create a secret password.</p>
<p>This method is mainly useful if you are 1 developer as you don't want to let the whole team know that you have just reset the database on your local machine.</p>
<p>Here is the basic use  of <code>UsersTableSeeder.php</code> which you can extend. Please note that I use the built ind authentication features of Laravel and have set up Mailgun in order to receive emails.</p>
<pre><code><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Carbon</span>\<span class="hljs-title">Carbon</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">Notifications</span>\<span class="hljs-title">ResetPassword</span> <span class="hljs-title">as</span> <span class="hljs-title">ResetPasswordNotification</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Hash</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Str</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">Passwords</span>\<span class="hljs-title">PasswordBroker</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UsersTableSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-comment">/**
     * Run the database seeds.
     *
     * <span class="hljs-doctag">@return</span> void
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>)
    </span>{
        $user = User::firstOrCreate([
            <span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">'John D.'</span>, <span class="hljs-comment">// Your display name</span>
            <span class="hljs-string">'email'</span> =&gt; <span class="hljs-string">'mail@domain.tld'</span>, <span class="hljs-comment">// Your email address</span>
            <span class="hljs-string">'password'</span> =&gt; Hash::make(Str::random(<span class="hljs-number">32</span>)), <span class="hljs-comment">// Password can't be empty so I generate a random non-known password.</span>
            <span class="hljs-string">'email_verified_at'</span> =&gt; Carbon::now(), <span class="hljs-comment">// I don't want to also have to verify the account, so I let it be verified per default.</span>
        ]);

        $user-&gt;notify(<span class="hljs-keyword">new</span> ResetPasswordNotification(app(PasswordBroker::class)-&gt;createToken($user))); <span class="hljs-comment">// This generates a token and sends the email</span>
    }
}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Fast webhook testing]]></title><description><![CDATA[Ever wanted to test a webhook or some other kind of broadcast event from your app?
Yesterday I discovered  webhook.site  which generates a unique URL for you and let you call that so you can see the calls in their interface and even heads and such.
T...]]></description><link>https://blog.emil.moe/fast-webhook-testing</link><guid isPermaLink="true">https://blog.emil.moe/fast-webhook-testing</guid><category><![CDATA[General Programming]]></category><category><![CDATA[General Advice]]></category><category><![CDATA[push notifications]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Sat, 09 Nov 2019 09:02:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1573290151766/TBwCVlKc7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever wanted to test a webhook or some other kind of broadcast event from your app?</p>
<p>Yesterday I discovered  <a target="_blank" href="https://webhook.site/">webhook.site</a>  which generates a unique URL for you and let you call that so you can see the calls in their interface and even heads and such.</p>
<p>The service doesn't require any registration and the basic service is free to use and should serve most needs.</p>
<h5 id="how-to-use-it">How to use it</h5>
<ol>
<li>Go to https://webhook.site/</li>
<li>Copy the URL marked in grey</li>
<li>Open the URL and watch in the main tab how the requests are displayed instantly - don't worry, the hook URL will always be a blank page.</li>
</ol>
<p>It's not my product, but this tool is a great help to me, I hope it can help somebody else too :-) </p>
<p> <a target="_blank" href="https://webhook.site/">https://webhook.site/</a> </p>
<hr />
<p>From the authors blog he has posted an article about what webhooks are:  <a target="_blank" href="https://simonfredsted.com/1583">What is a webhook?</a> </p>
]]></content:encoded></item><item><title><![CDATA[Fix the strange characters for (PHP) Artisan]]></title><description><![CDATA[I have lately been facing an issue where my artisan outputs a lot of strange characters such as ?[32m. It seems to be an issue with the latest Laravel.
Until now I have managed to read around it, but it still annoyed me.
Finally today I found the sol...]]></description><link>https://blog.emil.moe/fix-the-strange-characters-for-php-artisan</link><guid isPermaLink="true">https://blog.emil.moe/fix-the-strange-characters-for-php-artisan</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Fri, 08 Nov 2019 11:06:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1573211529510/9ubWJxxM5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have lately been facing an issue where my <code>artisan</code> outputs a lot of strange characters such as <code>?[32m</code>. It seems to be an issue with the latest Laravel.</p>
<p>Until now I have managed to read around it, but it still annoyed me.</p>
<p>Finally today I found the solution in this thread: https://laracasts.com/discuss/channels/laravel/php-artisan-not-working-4?page=1#reply=543852</p>
<h4 id="solution">Solution</h4>
<p>As <strong>Nakov</strong> says, you must have this package in your project to make it look correct again:</p>
<pre><code><span class="hljs-attribute">composer</span> require symfony/console:<span class="hljs-number">4</span>.<span class="hljs-number">3</span>.<span class="hljs-number">4</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[Store Laravel uploads off-site in the cloud]]></title><description><![CDATA[Long time ago uploads folders were common to any larger web project. A folder where uploaded files could be stored and a folder with 777 permission set (Read, Write, Execute on Linux).
For security reasons, having files or folders with write and exec...]]></description><link>https://blog.emil.moe/store-laravel-uploads-off-site-in-the-cloud</link><guid isPermaLink="true">https://blog.emil.moe/store-laravel-uploads-off-site-in-the-cloud</guid><category><![CDATA[DigitalOcean]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Tue, 05 Nov 2019 08:54:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1585126553354/-KwT9MY4I.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Long time ago uploads folders were common to any larger web project. A folder where uploaded files could be stored and a folder with <code>777</code> permission set (Read, Write, Execute on Linux).</p>
<p>For security reasons, having files or folders with write and execute permission is never an optimal solution while working with a website, but also as for risk management such as backup, it’s a better choice to use a storage service. Fortunately this is very easy and cheap to integrate into a Laravel project.</p>
<blockquote>
<p>The naming Amazon is used for simplicity here, although the actual name is AWS (Amazon Web Services) which is a subsidiary of Amazon.</p>
</blockquote>
<h1 id="storage-service">Storage Service</h1>
<p>Storage services or cloud storage as it is also called dates back to the beginning of the internet in mid-90s, but 10 years later when Amazon introduced their Simple Storage Service (S3) it became a lot easier to benefit from as they provide a simple API and tools to make scaling easy.</p>
<p>Other companies have since started to compete with Amazon. My favourite is DigitalOcean who has adapted the same API as Amazon uses. Whilst not being the only competitor with this feature, I believe DigitalOcean is a great platform for Laravel projects.</p>
<h2 id="amazon-s3">Amazon S3</h2>
<p>Since DigitalOcean uses the same API as Amazon all the configurations from this article will work for both. You will also realise that the settings in Laravel are referred to as S3. For this reason it’s rather simple to later migrate from DigitalOcean to AWS if you keep the endpoint as a constant in your <code>.env</code> for instance.</p>
<p>Read more about S3 at Amazon:  <a target="_blank" href="https://aws.amazon.com/s3/">https://aws.amazon.com/s3/</a> </p>
<h2 id="digitalocean-spaces">DigitalOcean Spaces</h2>
<p>The file storage solution, Spaces, from DigitalOcean works with an API as with Amazon. As mentioned earlier the API is the exact same, although with fewer advanced features.</p>
<p>The advantage of DigitalOcean is that you pay a fixed price per month, starting from $5, while at Amazon prices are fluid based on storage usage, data transfer and so on. It might be cheaper at first using Amazon, but the price changes dynamically for that reason. Larger projects with bigger budgets might have less overhead from Amazon though.</p>
<p>The fixed price includes a range of usage while exceeding this you will be charged, but it’s unlikely that you will exceed.</p>
<p>Read more about DigitalOcean Spaces:  <a target="_blank" href="https://www.digitalocean.com/products/spaces/">https://www.digitalocean.com/products/spaces/</a> </p>
<h1 id="configure-laravel">Configure Laravel</h1>
<p>Making Laravel ready for a storage service is straight forward and well described in the  <a target="_blank" href="https://laravel.com/docs/6.x/filesystem#driver-prerequisites">documentation</a> . I will give a brief description on how to get it running.</p>
<p>Since DigitalOcean utilises the Amazons API, you’ll have to install the AWS SDK in both cases</p>
<pre><code><span class="hljs-attribute">composer</span> require league/flysystem-aws-s<span class="hljs-number">3</span>-v<span class="hljs-number">3</span>
</code></pre><p>Next you need to add a few lines to <code>.env</code> to configure your storage api.</p>
<pre><code><span class="hljs-comment"># The secret generated from DigitalOcean API</span>
AWS_ACCESS_KEY_ID=
<span class="hljs-comment"># Key from DigitalOcean API</span>
AWS_SECRET_ACCESS_KEY=
<span class="hljs-comment"># Region ID for your Space (nyc2, sgp1, fra1, sfo2, ..)</span>
AWS_DEFAULT_REGION=
<span class="hljs-comment"># The unique name you chose for your Space</span>
AWS_BUCKET=
<span class="hljs-comment"># Full URL without name ({REGION}.digitaloceanspaces.com)</span>
AWS_URL=
<span class="hljs-comment"># Endpoint</span>
AWS_ENDPOINT=digitaloceanspaces.com
</code></pre><p>You must make one minor change to your filesystem driver in <code>config/filesystem.php</code> by adding the endpoint parameter:</p>
<pre><code><span class="hljs-string">'s3'</span> =&gt; [
  ...
  <span class="hljs-string">'endpoint'</span> =&gt; env(<span class="hljs-string">'AWS_ENDPOINT'</span>),
],
</code></pre><p>Now you can store files either per default to your cloud storage or when desired also locally.</p>
<h2 id="cloud-as-default">Cloud as default</h2>
<p>If you want to store everything on your cloud, the easiest is to set it as the default storage service in your <code>.env</code>:</p>
<pre><code><span class="hljs-attr">FILESYSTEM_DRIVER</span>=s3
</code></pre><p>Now you can access your cloud storage per default, without declaring a disk:</p>
<pre><code><span class="hljs-keyword">Storage</span>::..
</code></pre><p>You can still use the local driver by specifying it:</p>
<pre><code>Storage::disk(<span class="hljs-symbol">'local</span>')-&gt;..
</code></pre><h3 id="cloud-as-an-option">Cloud as an option</h3>
<p>If you rather want to use cloud as an optional feature and not default, you don’t have to do more. Instead you must refer to you the cloud disk when using it:</p>
<pre><code>Storage::disk(<span class="hljs-symbol">'cloud</span>')-&gt;..
</code></pre><h2 id="documentation">Documentation</h2>
<p>For full a full list of features for the Storage class read more in the official documentation: <a target="_blank" href="https://laravel.com/docs/7.x/filesystem">https://laravel.com/docs/7.x/filesystem</a> </p>
<h1 id="notes">Notes</h1>
<h2 id="cross-origin-resource-sharing-cors">Cross Origin Resource Sharing (CORS)</h2>
<p>CORS are probably only relevant to you if you plan to use DigitalOceans CDN feature. In most cases if files are streamed through you application it’s unnecessary to make any changes to these settings.</p>
<p>A few links about CORS
 <a target="_blank" href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">https://en.wikipedia.org/wiki/Cross-origin_resource_sharing</a> 
 <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS</a> </p>
<h2 id="file-listing">File listing</h2>
<p>You should in most cases disable file listing. It won’t prevent your API and Laravel application to list files on your Space, but it will prevent others from directly typing the URL to your Space and browse the files. File listing can be changed in your Space’s settings:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1585126843251/A2q_5MqKP.png" alt="Screenshot-2019-11-05-at-12.56.32.png" /></p>
<h2 id="limits">Limits</h2>
<p>There are a few limits to the Spaces, but these are unlikely to bother you if you have a small to a medium sized site. If you are facing the limits it’s most likely because you use Space as CDN without configuring it to be so. Read more about limits:  <a target="_blank" href="https://www.digitalocean.com/docs/spaces/#limits">https://www.digitalocean.com/docs/spaces/#limits</a> </p>
]]></content:encoded></item><item><title><![CDATA[Deploy Laravel packages to Packagist]]></title><description><![CDATA[You are probably already using Laravel when you read this, that means you are already using Packagist too, but might not be aware of it. In this guide you will learn how to deploy your own Laravel packages to Packagist.
Packagist is the platform behi...]]></description><link>https://blog.emil.moe/deploy-laravel-packages-to-packagist</link><guid isPermaLink="true">https://blog.emil.moe/deploy-laravel-packages-to-packagist</guid><category><![CDATA[composer]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Thu, 24 Oct 2019 13:38:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1585147621387/MiyFPCtVV.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You are probably already using Laravel when you read this, that means you are already using Packagist too, but might not be aware of it. In this guide you will learn how to deploy your own Laravel packages to Packagist.</p>
<p>Packagist is the platform behind Composer acting as the library. When you are searching for a new package for your project, you are probably doing this through Google, which works perfectly fine and I do so as well. But in fact you could do an even more targeted search on  <a target="_blank" href="Packagist.org">Packagist.org</a>.</p>
<h1 id="the-steps">The steps</h1>
<p>But how does Packagist work? Let’s split it into the steps necessary when creating a package for others to install through Composer:</p>
<ol>
<li>Developing the package itself</li>
<li>Hosting it on a git platform, Github will be the easiest</li>
<li>Submitting the repository to your Packagist account</li>
</ol>
<p>This article will not be covering how to develop a package, if you are interested in that part I can recommend my article I wrote for Hashnode:  <a target="_blank" href="https://blog.emilmoe.com/laravel-packages-how-to-build-and-deploy-ck226lpib002t1es1kwxp931a">Laravel Package – How to build and deploy</a> </p>
<p>There you will also find a brief description of deployment, but that will be covered in more details here.</p>
<h2 id="push-to-github">Push to Github</h2>
<p>If you already know how to push your package to Github, you can skip this step.</p>
<p>First you must create your repository on you Github account  <a target="_blank" href="https://github.com/new">https://github.com/new</a> . Rememeber it must be public in order to work with Packagist.</p>
<p>As Github states you must initialize your repository on your computer and push the files to Github.</p>
<pre><code>git init
git <span class="hljs-keyword">commit</span> -m <span class="hljs-string">"initial draft"</span>
git remote <span class="hljs-keyword">add</span> origin git@github.com:&lt;<span class="hljs-keyword">account</span>&gt;/&lt;repository&gt;.git
git push -u origin <span class="hljs-keyword">master</span>
</code></pre><h1 id="submitting-to-packagist">Submitting to Packagist</h1>
<p>Navigate to Packagist and click <strong>Submit</strong>  <a target="_blank" href="https://packagist.org/packages/submit">https://packagist.org/packages/submit</a>  and enter the URL of your repository</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1585147391210/ys1MWzza5.png" alt="image.png" /></p>
<p>Click check and submit.</p>
<h2 id="failed-to-submit">Failed to submit</h2>
<p>If you are receiving errors concerning <code>composer.json</code> this means you have an invalidate or missing the file in the root of your repository. The base of <code>composer.json</code> should contain:</p>
<pre><code>{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MyName/repository"</span>,
    <span class="hljs-attr">"authors"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Firstname Lastname"</span>,
            <span class="hljs-attr">"email"</span>: <span class="hljs-string">"email@address.com"</span>
        }
    ],
    <span class="hljs-attr">"require"</span>: {}
}
</code></pre><p>This can be created in your package with the command <code>composer init</code> and validated with <code>composer valid</code>.</p>
<h1 id="github-webhook">Github webhook</h1>
<p>The last step is to add a webhook to your Github repository in order for Packagist to be pinged every time you update your package.</p>
<p>Go to your repository’s <strong>Settings</strong>, then <strong>Webhooks</strong> and <strong>Add webhook</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1585147515395/4nANxXzeu.png" alt="image-1.png" /></p>
<p>Remember to update your username in the <strong>Payload URL</strong> parameter.</p>
<p>Your unique Packagist <strong>token</strong> can be found at your profile:  <a target="_blank" href="https://packagist.org/profile/">https://packagist.org/profile/</a> </p>
<p>Your package can now be installed by others</p>
<pre><code>composer require your-github-<span class="hljs-type">name</span>/your-repository-<span class="hljs-type">name</span>
</code></pre><h1 id="versioning">Versioning</h1>
<p>Packagist uses Git tags as version indicator. This means you could start your first tag as <code>1.0.0</code> then <code>1.0.1</code> and so on. Read more about semantic versioning at  <a target="_blank" href="https://semver.org/">https://semver.org/</a> </p>
<p>To add a tag and push the tags to your repository use the <code>--tags</code> parameter when you push</p>
<pre><code><span class="hljs-attribute">git</span> tag <span class="hljs-number">1</span>.<span class="hljs-number">0</span>.<span class="hljs-number">0</span>
<span class="hljs-attribute">git</span> push --tags
</code></pre><p>This can be a challenge in the beginning to keep up which version to tag. To list the tags used for a repository use the command without a value</p>
<pre><code><span class="hljs-attribute">git</span> tag
</code></pre><p>A graphical tool such as SourceTree can also be useful when managing tags, but everything can be done from the command line, of course.</p>
<hr />
<p>You are now set to share your packages with the world.</p>
]]></content:encoded></item><item><title><![CDATA[Backup MySQL to DigitalOcean]]></title><description><![CDATA[I have authored an article on how to create a dead simple backup approach for you project. My approach is for Laravel, but it could easily work for Wordpress as well or any other MySQL database for that instance as it only relies on MySQL and Linux c...]]></description><link>https://blog.emil.moe/backup-mysql-to-digitalocean</link><guid isPermaLink="true">https://blog.emil.moe/backup-mysql-to-digitalocean</guid><category><![CDATA[Laravel]]></category><category><![CDATA[MySQL]]></category><category><![CDATA[Backup]]></category><dc:creator><![CDATA[Emil Moe]]></dc:creator><pubDate>Wed, 23 Oct 2019 10:44:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1571827415745/TEI72SCfB.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have authored an article on how to create a dead simple backup approach for you project. My approach is for Laravel, but it could easily work for Wordpress as well or any other MySQL database for that instance as it only relies on MySQL and Linux commands.</p>
<p>If you are interested in the article it can be found here  <a target="_blank" href="https://emil.moe/2019/10/backup-laravel-easily-on-remote-server/">https://emil.moe/2019/10/backup-laravel-easily-on-remote-server/</a> </p>
<p>For this post I will share my final script for your convenience.</p>
<p>The script is</p>
<pre><code><span class="hljs-comment"># YOUR VARIABLES</span>
key=xxxxxxxxxxxxxxxxxxxx
secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
endpoint=xxxxx.digitaloceanspaces.com
bucket=<span class="hljs-keyword">my</span>-unique-name
dbuser=root
dbpassword=secret
dbname=database

<span class="hljs-comment"># MIGHT NOT NEED EDIT</span>
file=db-backup.sql
dbhost=<span class="hljs-number">127.0</span>.<span class="hljs-number">0</span>.<span class="hljs-number">1</span>

<span class="hljs-comment"># STATIC VARIABLES</span>
file=$(date <span class="hljs-string">"+%Y.%m.%d-%H.%M.%S"</span>).${file}
resource=<span class="hljs-string">"/<span class="hljs-subst">${bucket}</span>/<span class="hljs-subst">${file}</span>"</span>
contentType=<span class="hljs-string">"application/sql"</span>
dateValue=<span class="hljs-string">`date -R`</span>
stringToSign=<span class="hljs-string">"PUT\n\n<span class="hljs-subst">${contentType}</span>\n<span class="hljs-subst">${dateValue}</span>\n<span class="hljs-subst">${resource}</span>"</span>
signature=<span class="hljs-string">`/bin/echo -en ${stringToSign} | openssl sha1 -hmac ${secret} -binary | base64`</span>

<span class="hljs-comment"># DUMP SQL</span>
mysqldump --host=${dbhost} --user=${dbuser} --password=${dbpassword} ${dbname} &gt; ~<span class="hljs-regexp">/${file}

# UPLOAD
curl -X PUT -T ~/</span>${file} \
  -H <span class="hljs-string">"Host: <span class="hljs-subst">${bucket}</span>.<span class="hljs-subst">${endpoint}</span>"</span> \
  -H <span class="hljs-string">"Date: <span class="hljs-subst">${dateValue}</span>"</span> \
  -H <span class="hljs-string">"Content-Type: <span class="hljs-subst">${contentType}</span>"</span> \
  -H <span class="hljs-string">"Authorization: AWS <span class="hljs-subst">${key}</span>:<span class="hljs-subst">${signature}</span>"</span> \
  https:<span class="hljs-regexp">//</span>${bucket}.${endpoint}/${file}

<span class="hljs-comment"># REMOVE FILE AFTER UPLOAD</span>
rm ~<span class="hljs-regexp">/${file}
`</span>
</code></pre><p>And should be schedules with a cronjob:</p>
<pre><code>* *<span class="hljs-regexp">/4 * * * sh ~/</span>backup.sh &gt;<span class="hljs-regexp">/dev/</span><span class="hljs-literal">null</span> <span class="hljs-number">2</span>&gt;&amp;<span class="hljs-number">1</span>
</code></pre>]]></content:encoded></item></channel></rss>