<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Werner</title>
    <description>The latest articles on DEV Community by Daniel Werner (@daniel_werner).</description>
    <link>https://dev.to/daniel_werner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F185273%2Fe81dd0ab-c874-4449-94ef-11053408cbab.jpg</url>
      <title>DEV Community: Daniel Werner</title>
      <link>https://dev.to/daniel_werner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/daniel_werner"/>
    <language>en</language>
    <item>
      <title>How to implement translations with JetStream, Intertia and Vue3 using Laravel 10</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Tue, 15 Aug 2023 16:00:05 +0000</pubDate>
      <link>https://dev.to/daniel_werner/how-to-implement-translations-with-jetstream-intertia-and-vue3-using-laravel-10-2h8m</link>
      <guid>https://dev.to/daniel_werner/how-to-implement-translations-with-jetstream-intertia-and-vue3-using-laravel-10-2h8m</guid>
      <description>&lt;p&gt;Laravel 10 comes with internationalization support out of the box, however when we use JetStream starter pack with Inertia the transaction support is not included in the Vue part. Luckily it is quite easy to make it work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our use case
&lt;/h2&gt;

&lt;p&gt;A brand new Laravel 10 application, using JetStream with Intertia and Vue3 frontend. The app requires multilanguage support, so we need a solution that we can use in both frontend and backend code. The best would be to use the same language files for both. In the newest Laravel (in time of writing it is version 10) we can use both json files and php files for translations. The php files are located in &lt;strong&gt;lang/{locale}/file.php&lt;/strong&gt;. These files can be separated by "topic" like auth, validation etc. In case of json files, we only have one file per locale, and they are located in  &lt;strong&gt;resources/lang/{locale}.json&lt;/strong&gt; We want to use the latter because it is easily to pass the values to the frontend in json format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Default Laravel translation keys in json
&lt;/h2&gt;

&lt;p&gt;Laravel has a built in command to publish the default language files:  &lt;strong&gt;php artisan lang:publish ** Unfortunately this only publishes the php language files. To convert them to json I used a quick and dirty solution and replaced the return in those files by using **echo json_encode(['translation' =&amp;gt; 'content'])&lt;/strong&gt;, and copied over the json. One caveat here is that the php file name is the translation prefix (for example auth), so in the json you need to add auth. prefix to all the translation keys (for example: &lt;strong&gt;auth.password&lt;/strong&gt; ).&lt;/p&gt;

&lt;h2&gt;
  
  
  Share the translation data from the backend
&lt;/h2&gt;

&lt;p&gt;To be able to use the same language pack in both the frontend and the backend, we need pass the translation data to the frontend. First create a helper function which reads the json file &lt;strong&gt;support/helpers.php&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

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

if (! function_exists('translations')) {
    function translations($json)
    {
        if (!file_exists($json)) {
            return [];
        }

        return json_decode(file_get_contents($json), true);
    }
}

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

&lt;/div&gt;



&lt;p&gt;Add this file to the composer json autoload and run &lt;strong&gt;composer dump-autoload&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    "autoload": {
        "files": [
            "support/helpers.php"
        ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use Inertia so we share the translation object from the json by adding the following to the  &lt;strong&gt;app/Http/Middleware/HandleInertiaRequests.php&lt;/strong&gt; middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
   public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'locale' =&amp;gt; function () {
                return app()-&amp;gt;getLocale();
            },
            'translations' =&amp;gt; function () {
                return translations(
                    resource_path('lang/'. app()-&amp;gt;getLocale() .'.json')
                );
            },
        ]);
    }

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

&lt;/div&gt;



&lt;p&gt;This makes the translations available as Inertia props.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Vue3 plugin for translation
&lt;/h2&gt;

&lt;p&gt;The localization needs to be accessible everywhere so we create a plugin and make it accessible globally. Create a file  &lt;strong&gt;resources/js/Translator.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
export const Translator = {
    install: (v, translations) =&amp;gt; {
        v.config.globalProperties.__ = function(key, replace = {}) {
            let translation = translations[key]
                ? translations[key]
                : key;

            Object.keys(replace).forEach(function (key) {
                translation = translation.replaceAll(':' + key, replace[key])
            });

            return translation
        };
    },
};

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

&lt;/div&gt;



&lt;p&gt;This basically finds the given key in the translations object and replace the placeholders in the translation if there is any. We make our function " &lt;strong&gt;__&lt;/strong&gt;" available globally by adding it to the  *&lt;em&gt;globalProperties. *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For further details about Vue plugin development check out the docs here: &lt;a href="https://vuejs.org/guide/reusability/plugins.html#writing-a-plugin"&gt;https://vuejs.org/guide/reusability/plugins.html#writing-a-plugin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our plugin is ready, let's use it in our app:  &lt;strong&gt;resources/js/app.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import './bootstrap';
import '../css/app.css';

import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import {Translator} from './Translator.js';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';

createInertiaApp({
    title: (title) =&amp;gt; `${title} - ${appName}`,
    resolve: (name) =&amp;gt; resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
    setup({ el, App, props, plugin }) {

        return createApp({ render: () =&amp;gt; h(App, props) })
            .use(plugin)
            .use(ZiggyVue, Ziggy)
            .use(Translator, props.initialPage.props.translations)
            .mount(el);
    },
    progress: {
        color: '#4B5563',
    },
});

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using translations
&lt;/h2&gt;

&lt;p&gt;In blade files you can use the __() function as usual in Laravel, it will automatically find the proper json language file according to the app current locale. In Vue components use the same function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;p&amp;gt;{{__('auth.failed')}}&amp;lt;/p&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;When using options API you can use it in the code like: &lt;strong&gt;this.__('auth.failed')&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful resources
&lt;/h2&gt;

&lt;p&gt;I'd like to thank Dario Trbovic I took the original idea from his blog post: &lt;a href="https://dariot.medium.com/how-to-translate-laravel-with-inertia-c34c7284544e"&gt;https://dariot.medium.com/how-to-translate-laravel-with-inertia-c34c7284544e&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vue3 documentation about writing plugins: &lt;a href="https://vuejs.org/guide/reusability/plugins.html#writing-a-plugin"&gt;https://vuejs.org/guide/reusability/plugins.html#writing-a-plugin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Laravel documentation about localization: &lt;a href="https://laravel.com/docs/10.x/localization"&gt;https://laravel.com/docs/10.x/localization&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Planning poker app - a journey from Vue.js to Express through Socket.IO</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Mon, 29 May 2023 07:51:12 +0000</pubDate>
      <link>https://dev.to/daniel_werner/planning-poker-app-a-journey-from-vuejs-to-express-through-socketio-4pi7</link>
      <guid>https://dev.to/daniel_werner/planning-poker-app-a-journey-from-vuejs-to-express-through-socketio-4pi7</guid>
      <description>&lt;p&gt;In agile development process it is common to use planning poker to determine the story point of the tickets. There are plenty of free planning poker apps out there, but while using those we usually had some trouble with them. Either it cannot create a new game, or it just freezed and cannot restart, or any other. What every developer thinks in these cases? Let's build our own app, it is simple. Well, yeah, kind of... :)&lt;/p&gt;

&lt;h2&gt;
  
  
  How it should work?
&lt;/h2&gt;

&lt;p&gt;For the MVP we looked for the basic functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user creates a new game, and becomes an admin in that game&lt;/li&gt;
&lt;li&gt;Sharing the game url so the other players can join the game&lt;/li&gt;
&lt;li&gt;Every player gets a card where the points will appear&lt;/li&gt;
&lt;li&gt;Use fibonacci numbers for the possible points&lt;/li&gt;
&lt;li&gt;The admin has the ability to reveal the cards and start a new round&lt;/li&gt;
&lt;li&gt;When the cards are revealed it is not possible to change the points anymore&lt;/li&gt;
&lt;li&gt;The process should be interactive, every change should appear immediately for all the players&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which stack to use?
&lt;/h2&gt;

&lt;p&gt;The obvious choise for the frontend was Vue.js. I am using it on daily basis, and used in many projects before&lt;br&gt;&lt;br&gt;
For the interactivity we need websockets, thus the choice was Socket.IO, because it is a mature enough, battle tested library.&lt;br&gt;&lt;br&gt;
The backend uses Express.js server&lt;/p&gt;
&lt;h2&gt;
  
  
  How it was built?
&lt;/h2&gt;

&lt;p&gt;First step was to create a new Vue.js app with npm cli:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm init vue@latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Install Express:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install express&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Install Socket.IO&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install socket.io&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Using a single file for the backend stuff:  &lt;strong&gt;express.js&lt;/strong&gt;. We use vue router and have 3 routes to hanle: the main route (/), the game route (/game/hashid), and an about page (/about). The goal is to serve both the app and the socketio server on the same domain, we tell express to serve the vue app from public directory for both routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
app.use('/', express.static('public'));
app.use('/game/:id', express.static('public'));
app.use('/about', express.static('public'));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define a route in express and handle the new game generation, it generates a new uid for the game, store it in an object and return the id to the frontend. We also store the admin_id of the player who created the game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
app.post('/generate', (req, res) =&amp;gt; {
    const id = uid(16);

    games[id] = {
        admin_id: req.body.player_id,
        players: {}
    };

    res.send({id: id});
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to handle the connection of the players. We check if the game_id exists, and add the player to the games object. If the admin has joined we return  &lt;strong&gt;isAdmin: true&lt;/strong&gt; , to let the frontend know that the current player is an admin, and show  them the reveal and reset buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
io.on('connection', (socket) =&amp;gt; {
    let game_id = socket.handshake.auth.game_id;

    if(!games.hasOwnProperty(game_id)) {
        socket.disconnect();
        return;
    }

    let currentGame = games[game_id];

    socket.join(socket.handshake.auth.game_id);
    io.to(game_id).emit("players", currentGame.players);

    socket.on('join', (data, callback) =&amp;gt; {
        let name = data.name,
            player_id = socket.handshake.auth.player_id;

        games[game_id].players[player_id] = {player_id: player_id, name: name, points: null};
        io.to(game_id).emit("players", currentGame.players);

        callback({
            isAdmin: socket.handshake.auth.player_id === games[game_id].admin_id
        });
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next event we want to handle is when the players choose points. We find the user who sent the points, set the points to that user, and emit the whole players object to all the connected players.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
socket.on("points", (...args) =&amp;gt; {
    let data = args.pop();
    games[game_id].players[socket.handshake.auth.player_id].points = data.points;
    io.to(game_id).emit("players", currentGame.players);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We reached to the admin events, &lt;strong&gt;reveal&lt;/strong&gt; and &lt;strong&gt;reset&lt;/strong&gt;. In both cases we ensure that the event has come from the admin. Reveal is quite simple only sending the reveal event, and all the clients will show the point of the players:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
socket.on("reveal", (...args) =&amp;gt; {
    if(games[game_id].admin_id === socket.handshake.auth.player_id) {
        io.to(game_id).emit("reveal");
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the reset we delete the points of the users, send the reset and players events to the clients:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
socket.on("reset", (...args) =&amp;gt; {
    if(games[game_id].admin_id === socket.handshake.auth.player_id) {
        currentGame = games[game_id];

        for (let key in currentGame.players) {
            currentGame.players[key].points = null;
        }

        games[game_id] = currentGame;

        io.to(game_id).emit("reset");
        io.to(game_id).emit("players", currentGame.players);
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last but not least thing to handle is the disconnect event, where we remove the disconnected player for the data, and let everyone know that we don't have that player anymore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
socket.on('disconnect', () =&amp;gt; {
    socket.leave(socket.handshake.auth.game_id);
    delete games[socket.handshake.auth.game_id].players[socket.handshake.auth.player_id];
    io.to(game_id).emit("players", games[socket.handshake.auth.game_id].players);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this we finished the server part, let's take a look at some interesting parts of the frontend. I don't want to bore the reader with all the details of the desing and html, we'll just focus on the socket communication.&lt;/p&gt;

&lt;p&gt;Let's create a &lt;strong&gt;socket.js&lt;/strong&gt; where we create a state to store all the game related data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import { reactive } from "vue";
import { io } from "socket.io-client";

export const state = reactive({
    connected: false,
    greetEvents: [],
    players: {},
    isAdmin: false,
    game: {
        status: null
    }
});

export const socket = io('/', {autoConnect: false});

socket.on("connect", () =&amp;gt; {
    state.connected = true;
});

socket.on("disconnect", () =&amp;gt; {
    state.connected = false;
});

socket.on("players", (...args) =&amp;gt; {
    state.players = args.pop();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the player opens the home page, and starts a new game, we send and ajax request to the backend and if it was successfull, redirect the user to the game page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
generateGame() {
    if (!this.player_id) {
        this.player_id = uid(16);
    }

    axios.post('/generate', {
        player_id: this.player_id
    }).then(
        (response) =&amp;gt; {
            console.log(response)
            this.url = '/game/'+response.data.id;
            sessionStorage.setItem(response.data.id + '_player_id', this.player_id);
            state.isAdmin = true;
            this.$router.push('/game/'+response.data.id);
        });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this step a modal appears and the player can fill in their name and connect to the game. This step is handled in the mounted hook of the vue component. To prevent losing the active game when reloading the page, we store the player_id for the active game in the session storage, and use it to connect to the game if present (if a page reload happens).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
mounted() {

    this.game_id = this.$route.params.id;

    this.selectBonusPoint();

    this.player_id = sessionStorage.getItem(this.game_id + '_player_id');

    if (sessionStorage.getItem(this.game_id + '_player_name')) {

        this.name = sessionStorage.getItem(this.game_id + '_player_name');

    }

    if (!this.player_id) {

        this.player_id = uid(16);

        sessionStorage.setItem(this.game_id + '_player_id', this.player_id);

    }

    socket.auth = {

        game_id: this.$route.params.id,

        player_id: this.player_id

    };

    socket.connect();

    if (this.name) {

        this.joinGame();

    } else {

        this.showModal = true;

    }

}

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

&lt;/div&gt;



&lt;p&gt;Before we finish our journey let's check the join game method, which will send the payer's name to the backend to show up on the players card. This is the point where we receive and store the &lt;strong&gt;isAdmin&lt;/strong&gt; property to determine if our user is admin in the current game:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
joinGame() {
    socket.emit('join', {
        name: this.name
    }, (response) =&amp;gt; {
        state.isAdmin = response.isAdmin
    });

    sessionStorage.setItem(this.game_id + '_player_name', this.name);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This was my very first time playing with Socket.IO and the Express framework. I had fun developing this project, and I see a lot of possibilities using this technology for the future projects as well. If you are interested in the source code, it is open source you can find it here: &lt;a href="https://github.com/wdev-rs/planningpoker"&gt;https://github.com/wdev-rs/planningpoker&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Package development in Laravel - meetup with video</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Mon, 15 May 2023 10:06:39 +0000</pubDate>
      <link>https://dev.to/daniel_werner/package-development-in-laravel-meetup-with-video-384j</link>
      <guid>https://dev.to/daniel_werner/package-development-in-laravel-meetup-with-video-384j</guid>
      <description>&lt;p&gt;We've organized a meetup in &lt;a href="https://www.wdev.rs"&gt;WDev&lt;/a&gt; office on the topic of pakcage development in Laravel. We had great time exploring the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WHAT&lt;/strong&gt; are PHP packages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WHY&lt;/strong&gt; should we create packages?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HOW&lt;/strong&gt; to start?&lt;/li&gt;
&lt;li&gt;Versioning packages&lt;/li&gt;
&lt;li&gt;Usage with composer&lt;/li&gt;
&lt;li&gt;Laravel specific packages&lt;/li&gt;
&lt;li&gt;Developing Laravel packages&lt;/li&gt;
&lt;li&gt;Live coding examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are interested find the recording of the event here:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/6ooXqJjF-3o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Laravel analytics - how and why I made my own analytics package</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Wed, 26 Apr 2023 19:36:37 +0000</pubDate>
      <link>https://dev.to/daniel_werner/laravel-analytics-how-and-why-i-made-my-own-analytics-package-2i5k</link>
      <guid>https://dev.to/daniel_werner/laravel-analytics-how-and-why-i-made-my-own-analytics-package-2i5k</guid>
      <description>&lt;p&gt;I've used google analytics for couple of years and it worked quite well for me. The question arises why did I make my own analytics package. There were a couple of reasons to do so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google analytics became quite complex and slow lately. Especially with introduction of the new Google Analytics 4 it became more complex, and I realised that I don't use even 0.1 percent of its capability. This blog and the other websites I developed as side projects only need simple things like visitor count per day in a specific period, and page views for the top visited pages. That's it!&lt;/li&gt;
&lt;li&gt;I wanted to get rid of the third party cookies as much as possible&lt;/li&gt;
&lt;li&gt;Third party analytics tools are mostly blocked by ad blockers, so I see lower numbers than the real visitors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It needs to be a Laravel package, as I want to use it in couple of projects&lt;/li&gt;
&lt;li&gt;Keep it simple, only the basic functionality 

&lt;ul&gt;
&lt;li&gt;Track page visits by uri, and also the relevant model ids if applicable (for example blog post id, or product id)&lt;/li&gt;
&lt;li&gt;Save UserAgents for possible further analysis of visitor devices (desktop vs mobile) and to filter out bot traffic&lt;/li&gt;
&lt;li&gt;Save IP address for a planned feature: segment users by countries and cities&lt;/li&gt;
&lt;li&gt;"In house" solution, track the data in the applications own database&lt;/li&gt;
&lt;li&gt;Only backend functionality for tracking, no frontend tracking&lt;/li&gt;
&lt;li&gt;Create chart for visitors in the last 28 days, and most visited pages in the same period&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Build the MVP and push back any optional features, like 

&lt;ul&gt;
&lt;li&gt;Aggregate the data into separate table instead of querying the page_views table (I'll build it when the queries become slow)&lt;/li&gt;
&lt;li&gt;Add geoip databse, and save the user's country and city based on their IP&lt;/li&gt;
&lt;li&gt;Add possibility to change the time period shown on the charts&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  The database
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier the goal was to keep the whole thing very simple, so the database only consits of one table called laravel_analytics_page_views where the larave_analytics_ prefix is configurable in the config file to prevent potential conflicts with the app's databses tables.&lt;/p&gt;

&lt;p&gt;The schema structure/migration looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
$tableName = config('laravel-analytics.db_prefix') . 'page_views';

Schema::create($tableName, function (Blueprint $table) {
    $table-&amp;gt;id();
    $table-&amp;gt;string('session_id')-&amp;gt;index();
    $table-&amp;gt;string('path')-&amp;gt;index();
    $table-&amp;gt;string('user_agent')-&amp;gt;nullable();
    $table-&amp;gt;string('ip')-&amp;gt;nullable();
    $table-&amp;gt;string('referer')-&amp;gt;nullable()-&amp;gt;index();
    $table-&amp;gt;string('county')-&amp;gt;nullable()-&amp;gt;index();
    $table-&amp;gt;string('city')-&amp;gt;nullable();
    $table-&amp;gt;string('page_model_type')-&amp;gt;nullable();
    $table-&amp;gt;string('page_model_id')-&amp;gt;nullable();
    $table-&amp;gt;timestamp('created_at')-&amp;gt;nullable()-&amp;gt;index();
    $table-&amp;gt;timestamp('updated_at')-&amp;gt;nullable();

    $table-&amp;gt;index(['page_model_type', 'page_model_id']);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We track the unique visitors by session_id, which is of course not perfect and not 100% accurate but it does the job.&lt;/p&gt;

&lt;p&gt;We create a polymorpthic relation with page_model_type and page_model_id if there is a relevant model to the tracked page we save the type and the id to use in the future if necessary. Also created a combined index for these 2 fiels, as they are mostly queried together when using polymorphic relations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The middleware
&lt;/h2&gt;

&lt;p&gt;I wanted and universal solution rather than adding the analytics to all the controllers created a middleware which can handle the tracking. The middleware can be added to all routes or to specific group(s) of routes.&lt;/p&gt;

&lt;p&gt;The middleware itself is quite simple, it tracks only the get requests and skips the ajax calls. As it doesn't make sense to track bot traffic, I used the &lt;a href="https://github.com/JayBizzle/Crawler-Detect"&gt;https://github.com/JayBizzle/Crawler-Detect&lt;/a&gt; package to detect the crawlers and bots. When a crawler is detected it simply skips the tracking, this way we can avoid having useless data in the table.&lt;/p&gt;

&lt;p&gt;It was somewhat tricky how to get the associated model for the url in an universal way. The solution at the end is not totally universal because it assumes that the app uses route model binding and assumes that the first binding is relevant to that page. Again it is not perfect but it fits the minimalistic approach I followed while developing this package.&lt;/p&gt;

&lt;p&gt;Here is the code of the middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public function handle(Request $request, Closure $next)
{
    $response = $next($request);

    try {
        if (!$request-&amp;gt;isMethod('GET')) {
            return $response;
        }

        if ($request-&amp;gt;isJson()) {
            return $response;
        }

        $userAgent = $request-&amp;gt;userAgent();

        if (is_null($userAgent)) {
            return $response;
        }

        /** @var CrawlerDetect $crawlerDetect */
        $crawlerDetect = app(CrawlerDetect::class);

        if ($crawlerDetect-&amp;gt;isCrawler($userAgent)) {
            return $response;
        }

        /** @var PageView $pageView */
        $pageView = PageView::make([
            'session_id' =&amp;gt; session()-&amp;gt;getId(),
            'path' =&amp;gt; $request-&amp;gt;path(),
            'user_agent' =&amp;gt; Str::substr($userAgent, 0, 255),
            'ip' =&amp;gt; $request-&amp;gt;ip(),
            'referer' =&amp;gt; $request-&amp;gt;headers-&amp;gt;get('referer'),
        ]);

        $parameters = $request-&amp;gt;route()?-&amp;gt;parameters();
        $model = null;

        if (!is_null($parameters)) {
            $model = reset($parameters);
        }

        if (is_a($model, Model::class)) {
            $pageView-&amp;gt;pageModel()-&amp;gt;associate($model);
        }

        $pageView-&amp;gt;save();

        return $response;
    } catch (Throwable $e) {
        report($e);
        return $response;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The routes
&lt;/h2&gt;

&lt;p&gt;When developing Laravel packages it is possible to set up the package service provider to tell the application to use the routes from the package. I usually don't use this approach, because this way in the application you don't have much control over the routes: for example you cannot add prefix, put them in an group or add middleware to them. &lt;/p&gt;

&lt;p&gt;I like to create a class with a static method routes, where I define all the routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public static function routes()
{

    Route::get(
        'analytics/page-views-per-days',
        [AnalyticsController::class, 'getPageViewsPerDays']
    );

    Route::get(
        'analytics/page-views-per-path',
        [AnalyticsController::class, 'getPageViewsPerPaths']
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way I could easily put the package routes under the /admin part in my application for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The frontend components
&lt;/h2&gt;

&lt;p&gt;The frontend part consists of 2 vue components one for the visitor chart and one contains a simple table of the most visited pages. For the chart I used the Vue chartjs library (&lt;a href="https://github.com/apertureless/vue-chartjs"&gt;https://github.com/apertureless/vue-chartjs&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

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



&amp;lt;template&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;div&amp;gt;&amp;lt;strong&amp;gt;Visitors: {{chartData.datasets[0].data.reduce((a, b) =&amp;gt; a + b, 0)}}&amp;lt;/strong&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;LineChartGenerator
                :chart-options="chartOptions"
                :chart-data="chartData"
                :chart-id="chartId"
                :dataset-id-key="datasetIdKey"
                :plugins="plugins"
                :css-classes="cssClasses"
                :styles="styles"
                :width="width"
                :height="height"
            /&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;

import { Line as LineChartGenerator } from 'vue-chartjs/legacy'
import {
    Chart as ChartJS,
    Title,
    Tooltip,
    Legend,
    LineElement,
    LinearScale,
    CategoryScale,
    PointElement
} from 'chart.js'

ChartJS.register(
    Title,
    Tooltip,
    Legend,
    LineElement,
    LinearScale,
    CategoryScale,
    PointElement
)

export default {
    name: 'VisitorsPerDays',
    components: { LineChartGenerator },
    props: {
        'initialData': Object,
        'baseUrl': String,
        chartId: {
            type: String,
            default: 'line-chart'
        },
        datasetIdKey: {
            type: String,
            default: 'label'
        },
        width: {
            type: Number,
            default: 400
        },
        height: {
            type: Number,
            default: 400
        },
        cssClasses: {
            default: '',
            type: String
        },
        styles: {
            type: Object,
            default: () =&amp;gt; {}
        },
        plugins: {
            type: Array,
            default: () =&amp;gt; []
        }
    },
    data() {
        return {
            chartData: {
                labels: Object.keys(this.initialData),
                datasets: [
                    {
                        label: 'Visitors',
                        backgroundColor: '#f87979',
                        data: Object.values(this.initialData)
                    }
                ]
            },
            chartOptions: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        ticks: {
                            precision: 0
                        }
                    }
                }
            }
        }
    },
    mounted() {
    },

    methods: {

    },
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;It was quite fun and interesting project and after using it for about an month and analysing the results, it seem to be working fine. If you are interested in the code, or would like to try the package feel free to check it out on GitHub here: &lt;a href="https://github.com/wdev-rs/laravel-analytics"&gt;https://github.com/wdev-rs/laravel-analytics&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Let's do TDD (with video)</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Wed, 23 Nov 2022 19:00:00 +0000</pubDate>
      <link>https://dev.to/daniel_werner/lets-do-tdd-with-video-4pba</link>
      <guid>https://dev.to/daniel_werner/lets-do-tdd-with-video-4pba</guid>
      <description>&lt;p&gt;I am a big fan of Test Driven Development and trying to promote it on every possible occassion. I am doing it because having a good test suite makes me confortable when refactoring or making changes in my code. Also it saved me a couple of times from creating a hidden bug while implementing or changing something.&lt;/p&gt;

&lt;p&gt;In this blog post I'll guide you through the process how to start with TDD in any PHP project, how to install phpunit, and even how to make it fun. But let's start with a short introduction about the test driven development.&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR;
&lt;/h2&gt;

&lt;p&gt;If you prefer video content scroll down to the bottom, there is a recording from a meetup, where I held a talk about this topic, or just &lt;a href="https://www.youtube.com/watch?v=ZkhbAIro9PQ"&gt;click here&lt;/a&gt; to watch it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why developers don't write tests?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  We don't have time for it
&lt;/h3&gt;

&lt;p&gt;That is the most common escuse for not writing tests. I can understand the time concern. Developers are usually under time pressure, and don't want to waste more time than necessary for a feature. But. You still have to think about the feature before you start implementing it, right? So while thinkig you could also write down your thoughts in a test. I admit you need some experience to quickly write the test cases, and in the beginning it will take some time. In my oppinion it still worth it. Every new thing you learn takes some time.&lt;/p&gt;

&lt;h3&gt;
  
  
  It is complicated
&lt;/h3&gt;

&lt;p&gt;Yes, it might be. For the first couple of tests, afterwards it becomes a more streamlined process.&lt;/p&gt;

&lt;h3&gt;
  
  
  It is boring
&lt;/h3&gt;

&lt;p&gt;Yes, if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;write the tests after the feature, it is definitely boring.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;write it beforehand it is less boring, but still not fun&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;do real test driven development, it can even be fun. I'll show you that later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  I already tested it manually
&lt;/h3&gt;

&lt;p&gt;Yes, we all do. And it is still necessary, the TDD won't replace all the manual testing. My problem with this approach is: do you test all the other, possibly related features. Do you notice, that while implementing a new feature, you might introduced a bug?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we need tests?
&lt;/h2&gt;

&lt;p&gt;It depends on the situation and the project. Not always necessary. If we make a critical, urgent hotfix. If the feature/fix is so simple that testing would make an unnecessary overhead. Developing a feature that is temporary, or likely to change soon. A feature that is not an important part of the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy and confident refactoring
&lt;/h3&gt;

&lt;p&gt;Have you been in a situation when all of you knew that a piece of code should be refactored, but everyone was afraid to touch it? I've been there couple of times.&lt;/p&gt;

&lt;p&gt;The best thing a about test driven development is that with a good test coverage, refactoring the code becomes easy. When you can rely on your tests, you can confidently refactor the code and assure you don't change the functionality or break something.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prevent bugs
&lt;/h3&gt;

&lt;p&gt;Many times doing TDD you can think of or find bugs that are edge cases and can not be easily catched by manual testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prevent breaking something else
&lt;/h3&gt;

&lt;p&gt;In bigger systems components can be tied together in mutliple ways. Changing some part of the code might break something else. It is very nice when such things happens the test suite warns you about the mistake, and the broken code doesn't leave your machine, and doesn't create problems in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test cases are reproducible
&lt;/h3&gt;

&lt;p&gt;And last but not least, test cases are reproducible. You can run them as many times you want (best to run it on a CI). Who wants to test manually all the features of a web app after each push? No, I really don't :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, but what kind of tests?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unit tests
&lt;/h3&gt;

&lt;p&gt;As the name suggests they are meant to test a small piece of code (units), for example a class or a function. Their advantage is that the are fast, and cheap (in terms of CI running time). As you can see from the testing  pyramid below, they are at the bottom, we should have a lot of them :-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration tests
&lt;/h3&gt;

&lt;p&gt;They are made to test some functionality of the app. For example test an api endpoint, or a controller action. They usually need database (or a fake/test database) and are more slow and expensive that unit tests. They should be responsible to assure the different components are working nicely together. We need less of these than unit tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  E2E tests
&lt;/h3&gt;

&lt;p&gt;Test everything from frontend to backend (End To End), usually these are browser tests. These test ensure that the whole system works as expected from user experience, to backed functionalities. They are slow and expensive. My advice is that you only write them for the most critial parts of the system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9IS6MG4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.danielwerner.dev/images/4ihzyLXKqHg5tUyQsCqTQHV0OSYgEgcqkuXBV7Fe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9IS6MG4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.danielwerner.dev/images/4ihzyLXKqHg5tUyQsCqTQHV0OSYgEgcqkuXBV7Fe.png" alt="Testing pyramid" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's see some CODE already!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting up PHPUnit
&lt;/h3&gt;

&lt;p&gt;I assume that your project already uses composer and PSR-4 autoloading. So let' install phpunit:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require --dev phpunit/phpunit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We install phpunit with --dev option to make it a dev dependency only. We don't need phpunit in production, and we can skip it in deploy process for example.&lt;/p&gt;

&lt;p&gt;Our composer json looks similar to this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
{
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "require-dev": {
        "phpunit/phpunit": "^9.5"
    }
}

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

&lt;/div&gt;



&lt;p&gt;Our application classes live in the app directory using the App namespace, and our tests live in the tests directory using Tests namespace.&lt;/p&gt;

&lt;p&gt;We create the basic phpunit.xml to instruct phpunit how to run our tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         bootstrap="vendor/autoload.php"
         colors="true"
&amp;gt;
    &amp;lt;testsuites&amp;gt;
        &amp;lt;testsuite name="Unit"&amp;gt;
            &amp;lt;directory suffix=".php"&amp;gt;./tests&amp;lt;/directory&amp;gt;
        &amp;lt;/testsuite&amp;gt;
    &amp;lt;/testsuites&amp;gt;
    &amp;lt;php&amp;gt;
        &amp;lt;env name="APP_ENV" value="testing"/&amp;gt;
    &amp;lt;/php&amp;gt;
&amp;lt;/phpunit&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;We created one test suite a named it "Unit", which runs all the .php files from the "tests" directory. If you are interested in more details about phpunit configuration, please check the phpunit &lt;a href="https://phpunit.readthedocs.io/en/9.5/configuration.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the first tests
&lt;/h3&gt;

&lt;p&gt;When doing test drive development, we do the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a failing test (but only the bare minimum - for the first step)&lt;/li&gt;
&lt;li&gt;Write the code which makes the test pass (but only write that much code, which satisfies the test, no more)&lt;/li&gt;
&lt;li&gt;Add more assertion to the test, so it fails again&lt;/li&gt;
&lt;li&gt;Implement the required feature&lt;/li&gt;
&lt;li&gt;Repeat until the feature is complete&lt;/li&gt;
&lt;li&gt;Refactor the code, and make sure the tests still pass&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets create our first test in test/Unit/CartTest.php:&lt;br&gt;
&lt;/p&gt;

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

namespace Unit;

use PHPUnit\Framework\TestCase;
use App\Cart;

class CartTest extends TestCase
{
    public function testAddItemToCart()
    {
        $cart = new Cart();

        $this-&amp;gt;assertNotNull($cart);
    }
}

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

&lt;/div&gt;



&lt;p&gt;Run the phpunit:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vendor/bin/phpunit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The test will obviously fail, because we don't even have the Cart class, so create the class in app/Cart.php&lt;br&gt;
&lt;/p&gt;

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

namespace App;

class Cart
{

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

&lt;/div&gt;



&lt;p&gt;Run the phpunit again, and our tests should pass now&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vendor/bin/phpunit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our next step is to implement some functions to the cart. So lets expand our test and make it fail again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public function testAddItemToCart()
{
    $cart = new Cart();

    $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets add the $items property and an addItem method to the Cart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
class Cart
{
    protected array $items = [];

    public function addItem(string $code, string $name, int $quantity, int $price)
    {
        $this-&amp;gt;items[] = [
            'code' =&amp;gt; $code,
            'name' =&amp;gt; $name,
            'quantity' =&amp;gt; $quantity,
            'price' =&amp;gt; $price
        ];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our test passes again, but we didn't do any proper assertation to makes sure that the method does what it should. So let's do this, assert that the number of the items in the cart equals to 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
public function testAddItemToCart()
{
    $cart = new Cart();

    $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);

    $this-&amp;gt;assertCount(1, $cart-&amp;gt;getItems());
    $this-&amp;gt;assertNotNull($cart);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run this test again, it fails, because we don't have getItems method yet, so let's create it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
/**
 * @return array
 */
public function getItems(): array
{
    return $this-&amp;gt;items;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the test will pass, and this way we developed the first version of the method using TDD.&lt;/p&gt;

&lt;p&gt;Now we have the ability to add items to cart, our next step is to implement a method to remove items from the cart. So we create a test for it. As a first step we create the test method without any assertion just call the remove items method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function testRemoveItemFromCart()
    {
        $cart = new Cart();
        $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);         
        $cart-&amp;gt;removeItem('P111');     
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have failing test now because the method doesn't exist yet. Let's create it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function removeItem(string $code)
    {
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created the dummy method, but our test is not erroring out anymore. Now we add the assertion to the test and actually implement the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function testRemoveItemFromCart()
    {
        $cart = new Cart();
        $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);

        $cart-&amp;gt;removeItem('P111');

        $this-&amp;gt;assertCount(0, $cart-&amp;gt;getItems());
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we do a very basic implementation of the removeItem method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function removeItem(string $code)
    {
        foreach ($this-&amp;gt;items as $index =&amp;gt; $item)
        {
            if ($item['code'] === $code){
                unset($this-&amp;gt;items[$index]);
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic implementation, but it works, and the test passes. Now let's refactor it. As we already have test coverage, we can do it confidently, knowing that the functionality remanins the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function removeItem(string $code)
    {

        $this-&amp;gt;items = array_filter($this-&amp;gt;items, fn($item) =&amp;gt; $item['code'] !== $code);

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

&lt;/div&gt;



&lt;p&gt;The next step is to make our cart "smarter", so when we add multiple items with the same code, it increments the quantilty instead of adding a new item to the array, so we create the test first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function testAddSameItemMutlipleTimesIncreasesQuantity()
    {
        $cart = new Cart();

        $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);
        $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);

        $expected = [
            [
                'code' =&amp;gt; 'P111',
                'name' =&amp;gt; 'Pizza',
                'quantity' =&amp;gt; 2,
                'price' =&amp;gt; 110000
            ]
        ];

        $this-&amp;gt;assertEquals($expected, $cart-&amp;gt;getItems());
    }

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

&lt;/div&gt;



&lt;p&gt;This failes becaue the current cart will add the item twice to the array. So we are changing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function addItem(string $code, string $name, int $quantity, int $price)
    {
        foreach($this-&amp;gt;items as $index =&amp;gt; $item) {
            if ($item['code'] === $code){
               $this-&amp;gt;items[$index]['quantity'] += $quantity;
               return;
            }
        }

        $this-&amp;gt;items[] = [
            'code' =&amp;gt; $code,
            'name' =&amp;gt; $name,
            'quantity' =&amp;gt; $quantity,
            'price' =&amp;gt; $price
        ];
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added the foreach to check if we already have that item in the cart, and if yes, we increase the quantity. Otherwise just add the item to the items. Now the all the tests will pass. As we added this new feature to the add items, we need to modify the remove items as well to support this functionality. &lt;/p&gt;

&lt;p&gt;We create a test, and add two different items a pizza and two beers :), and assert the removal of one beer decrements the quantity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function testRemoveItemDecreasesQuantityOrRemovesItem()
    {
        $cart = new Cart();
        $cart-&amp;gt;addItem('P111', 'Pizza', 1, 110000);
        $cart-&amp;gt;addItem('B111', 'Beer', 2, 10000);

        $cart-&amp;gt;removeItem('B111');

        $expected = [
            [
                'code' =&amp;gt; 'P111',
                'name' =&amp;gt; 'Pizza',
                'quantity' =&amp;gt; 1,
                'price' =&amp;gt; 110000
            ],
            [
                'code' =&amp;gt; 'B111',
                'name' =&amp;gt; 'Beer',
                'quantity' =&amp;gt; 1,
                'price' =&amp;gt; 10000
            ]
        ];

        $this-&amp;gt;assertEquals($expected, $cart-&amp;gt;getItems());
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have this failing test we change the removeItem method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function removeItem(string $code)
    {
        foreach($this-&amp;gt;items as $index =&amp;gt; $item) {
            if ($item['code'] === $code){
                $this-&amp;gt;items[$index]['quantity']--;
                return;
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running our new test passes, but oops, we introduced  a bug! Running a whole test suite we notice that our previous test for removeItem fails. &lt;/p&gt;

&lt;p&gt;Let's fix that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    public function removeItem(string $code)
    {
        foreach($this-&amp;gt;items as $index =&amp;gt; $item) {
            if ($item['code'] === $code){
                if ($item['quantity'] &amp;gt; 1){
                    $this-&amp;gt;items[$index]['quantity']--;
                }
                else {
                    unset($this-&amp;gt;items[$index]);
                }

                return;
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all out tests should pass, and we have a working cart with add and remove functions. This example proves the point that TDD can prevent itroducing bugs in the existing functionality.&lt;/p&gt;

&lt;p&gt;Of course we can add more assertions and more features to it, but it is out of scope for this blog post, my goal was to show the method how the TDD can be part of the process of writing code and even be fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some useful resources
&lt;/h2&gt;

&lt;p&gt;If you'd like to learn more about testing, I can recommend to check out the following links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://phpunit.de/documentation.html"&gt;https://phpunit.de/documentation.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://laravel.com/docs/9.x/testing"&gt;https://laravel.com/docs/9.x/testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code of the above example, and a Laravel example on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/daniel-werner/lets-do-tdd-php"&gt;https://github.com/daniel-werner/lets-do-tdd-php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/daniel-werner/lets-do-tdd-laravel"&gt;https://github.com/daniel-werner/lets-do-tdd-laravel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We held a meetup on this topic, please find the slides from the meetup below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/daniel-werner/lets-do-tdd-php/blob/main/presentation/Lets%20do%20tdd.pdf"&gt;https://github.com/daniel-werner/lets-do-tdd-php/blob/main/presentation/Lets%20do%20tdd.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If interested in the meetup, please find the video of the presentation below:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZkhbAIro9PQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Under the hood: How states work in Laravel factories</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Sat, 06 Nov 2021 11:46:04 +0000</pubDate>
      <link>https://dev.to/daniel_werner/under-the-hood-how-states-work-in-laravel-factories-34gh</link>
      <guid>https://dev.to/daniel_werner/under-the-hood-how-states-work-in-laravel-factories-34gh</guid>
      <description>&lt;p&gt;Since Laravel 8 we can use the new and reworked model factories. They allow us to create multiple interconnected model instances using a simple and easy to read syntax. As usual Laravel has a great documentation about how to create and write these factories. In this article I don’t describe the usage of the factories, if you are interested in that check out the &lt;a href="https://laravel.com/docs/8.x/database-testing#defining-model-factories"&gt;documentation&lt;/a&gt;. Instead we will go a little deeper and understand how the states work in factories. Also we will show an interesting example we run into lately, where the states worked differently as we expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the states?
&lt;/h2&gt;

&lt;p&gt;From the documentation: “State manipulation methods allow you to define discrete modifications that can be applied to your model factories in any combination.” Basically it allows us to define for example a status of the model you create, or basically predefine any of the model attributes.&lt;/p&gt;

&lt;p&gt;According to the documentation you should pass a closure to the state method, however you can also pass a simple array, because it will wrap it into a closure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function state($state)
{
    return $this-&amp;gt;newInstance([
        'states' =&amp;gt; $this-&amp;gt;states-&amp;gt;concat([
            is_callable($state) ? $state : function () use ($state) {
                return $state;
            },
        ]),
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you define a sequence it basically also add a state on the factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function sequence(...$sequence)
{
    return $this-&amp;gt;state(new Sequence(...$sequence));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the factory creates a new model instance it will call the state callback, to apply the changes to the model attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected function getRawAttributes(?Model $parent)
{
    return $this-&amp;gt;states-&amp;gt;pipe(function ($states) {
        return $this-&amp;gt;for-&amp;gt;isEmpty() ? $states : new Collection(array_merge([function () {
            return $this-&amp;gt;parentResolvers();
        }], $states-&amp;gt;all()));
    })-&amp;gt;reduce(function ($carry, $state) use ($parent) {
        if ($state instanceof Closure) {
            $state = $state-&amp;gt;bindTo($this);
        }

        return array_merge($carry, **$state($carry, $parent)**);
    }, $this-&amp;gt;definition());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What has tricked us?
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier is is also possible to pass a simple array to the state, and it works fine with static values, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt;state(['status' =&amp;gt; 'draft'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case we wanted to create multiple instances of a model, and pass a state which is generated by faker with some special rules like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt;count(3)-&amp;gt;state(['value' =&amp;gt; $this-&amp;gt;faker-&amp;gt;numberBetween(0, 10)])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is obviously a simplified example in the real case we did multiple instances with the same faker with different conditions. And here comes the trick. It generated all the instances with the same value. Why this has happened? As we saw earlier when you pass a non callable argument to the state it will wrap it into a closure. And basically the faker will be called only once, when you pass the array, and the generated closure for the state will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function () {
    return ['value' =&amp;gt; 4];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this state closure will be executed over and over again so it will return the same value for all models. But when we realised this, and passed a closure our closure looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function () {
    return ['value' =&amp;gt; $this-&amp;gt;faker-&amp;gt;numberBetween(0, 10)];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and in this case the faker is executed for all generated models separately, and generated different random values as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we could have done differently?
&lt;/h2&gt;

&lt;p&gt;In this case we could use sequences. Those are basically intended for these purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt;state(new Sequence(
    fn ($sequence) =&amp;gt; ['value' =&amp;gt; $this-&amp;gt;faker-&amp;gt;numberBetween(0, 10)],
))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted to show one interesting thing for the end of this article. As we already saw the sequence instance is passed to the state. The sequence is not wrapped into a closure because it has an &lt;strong&gt;__invoke()&lt;/strong&gt; method and the php built in &lt;strong&gt;is_callable($state)&lt;/strong&gt; method will return true for those invokable classes.&lt;/p&gt;

&lt;p&gt;Hope this article helps you understanding the mechanics behind the factory states and to avoid the same mistake we did.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/under-the-hood-how-states-work-in-laravel-factories/"&gt;Under the hood: How states work in Laravel factories&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>underthehood</category>
      <category>factories</category>
      <category>factorystate</category>
    </item>
    <item>
      <title>Testing with Mocha – 5. BDD style assertions with Chai</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Thu, 22 Apr 2021 06:56:50 +0000</pubDate>
      <link>https://dev.to/daniel_werner/testing-with-mocha-5-bdd-style-assertions-with-chai-ac4</link>
      <guid>https://dev.to/daniel_werner/testing-with-mocha-5-bdd-style-assertions-with-chai-ac4</guid>
      <description>&lt;p&gt;Chai is a TDD and BDD library, and comes with several flavors for assertions: should, expect and assert. In the following lessons we will take a look at both styles and see how to use them in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation and first example
&lt;/h2&gt;

&lt;p&gt;First we need to install chai using a package manager, for example npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
npm install chai

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

&lt;/div&gt;



&lt;p&gt;When it is done, let’s take a look at our first example with chai.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/abpQaBb"&gt;Testing with Mocha – Example 5.1&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we see in this example we are using expect instead of assert, let’s see some more details about the different assertion styles, which can be used in Chai.&lt;/p&gt;

&lt;p&gt;The BDD style assertions are &lt;code&gt;expect&lt;/code&gt; and &lt;code&gt;should&lt;/code&gt;. Both use the chainable getters to create nice and readable assertions.&lt;/p&gt;

&lt;p&gt;The basic difference between expect and should is how they create the chain of expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expect
&lt;/h2&gt;

&lt;p&gt;Using expect you have to wrap the asserted value with and &lt;code&gt;expect()&lt;/code&gt; function call, and create the chainable assertion afterwards. Let’s see an example.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/abpQaBb"&gt;Testing with Mocha – Example 5.1&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should
&lt;/h2&gt;

&lt;p&gt;Should provides the same chainable interface as expect, but should extends all the objects with a should property. This should property can be used to create the assert chain.&lt;/p&gt;

&lt;p&gt;Let’s see the previous example rewritten with should.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/NWdELgB"&gt;Testing with Mocha – Example 5.2&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Important to notice, that as opposed to expect should function should actually be called after requiring it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const should = require('chai').should()

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

&lt;/div&gt;



&lt;p&gt;When using &lt;code&gt;should&lt;/code&gt; with ES2015 style imports, the function call should go on separate line, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import chai from 'chai';
chai.should();

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

&lt;/div&gt;



&lt;p&gt;As opposed to equal, which only needs to be required/imported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const expect = require('chai').expect;

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

&lt;/div&gt;



&lt;p&gt;For more information about should and expect style assertions check the &lt;a href="https://www.chaijs.com/guide/styles/"&gt;Chai documentation&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Check out all posts in the series&lt;/em&gt; &lt;strong&gt;&lt;a href="https://www.danielwerner.dev/category/testing-with-mocha/"&gt;Testing with Mocha&lt;/a&gt;&lt;/strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/testing-with-mocha-5-bdd-style-assertions-with-chai/"&gt;Testing with Mocha – 5. BDD style assertions with Chai&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testingwithmocha</category>
      <category>bdd</category>
      <category>chai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Testing with Mocha – 4. Testing error handling</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Tue, 20 Apr 2021 06:46:20 +0000</pubDate>
      <link>https://dev.to/daniel_werner/testing-with-mocha-4-testing-error-handling-32pm</link>
      <guid>https://dev.to/daniel_werner/testing-with-mocha-4-testing-error-handling-32pm</guid>
      <description>&lt;p&gt;In this post we will take a look how can we expect that an error is thrown and how to make sure no error is thrown when when everything should be fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asserting an error is thrown
&lt;/h2&gt;

&lt;p&gt;Let’s take a look on how to make sure the expected error is thrown in case when we need it. As an example lets extend our generic collection class, and create a specific collection which can store only numeric values.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/poRxjmG"&gt;Testing with Mocha – Example 4.1&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we can see in the example above, the numeric collection throws and error when we try to push a non numeric value in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    if (typeof item !== 'number') {
        throw new TypeError('Numeric collection can only store numbers!')
    }

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

&lt;/div&gt;



&lt;p&gt;In the test we make sure, that this specific error occurs when we try to push a string to our numeric collecion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
     assert.throws(
          () =&amp;gt; collection.push("4"),
          TypeError
      );

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Asserting an error is not thrown
&lt;/h2&gt;

&lt;p&gt;As we’ve seen in the previous example we can assert that a specific error is thrown when necessary. We can also make sure that the error is not thrown when everything should work fine. The following example shows that the numeric collection accepts an integer value without throwing the type error.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/OJWBMJv"&gt;Testing with Mocha – Example 4.2&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you can see, this example is almost the same as the previous. The only important difference is that we pushed an integer to the numeric collection,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
collection.push(4)

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

&lt;/div&gt;



&lt;p&gt;and we asserted that the code does not throw a TypeError:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
         assert.doesNotThrow(
              () =&amp;gt; collection.push(4),
              TypeError
          );

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

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Check out all posts in the series&lt;/em&gt; &lt;strong&gt;&lt;a href="https://www.danielwerner.dev/category/testing-with-mocha/"&gt;Testing with Mocha&lt;/a&gt;&lt;/strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/testing-with-mocha-4-testing-error-handling/"&gt;Testing with Mocha – 4. Testing error handling&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testingwithmocha</category>
      <category>bdd</category>
      <category>javascript</category>
      <category>mocha</category>
    </item>
    <item>
      <title>Testing with Mocha – 3. Testing asynchronous code</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Wed, 14 Apr 2021 06:23:02 +0000</pubDate>
      <link>https://dev.to/daniel_werner/testing-with-mocha-3-testing-asynchronous-code-1069</link>
      <guid>https://dev.to/daniel_werner/testing-with-mocha-3-testing-asynchronous-code-1069</guid>
      <description>&lt;p&gt;In JavaScript we often write asynchronous code, for example ajax calls, database operations etc. When testing our code we want to be able to test this asynchronous parts of our code as well. Let’s see how we can achieve this when testing with Mocha.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous code with callbacks
&lt;/h2&gt;

&lt;p&gt;For testing async code which uses callbacks we can use Mocha’s &lt;code&gt;done&lt;/code&gt; function. When passing &lt;code&gt;done&lt;/code&gt; in the test Mocha knows that it should wait for that callback to be called. Let’s see and example. Let’s add a save method to our collection, which will operate asynchronously.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/YzNaMNo"&gt;Testing with Mocha – Example 3.1&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous code with Promises
&lt;/h2&gt;

&lt;p&gt;If our code use promises instead of callback we can use doesNotReject assertion. Note: when testing promises  &lt;strong&gt;do not&lt;/strong&gt;  use the &lt;code&gt;done&lt;/code&gt; callback. Let’s see and example how to test promises. We’ll “reimplement” the save method of our collection to use promises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt;  When asserting promises, you should  &lt;strong&gt;return&lt;/strong&gt;  the result of the assertion for Mocha to pick them up. Otherwise you’d in case when test should not pass, it passes, but you’d get an error.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/bGgvJYg"&gt;bGgvJYg&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous code with async/await
&lt;/h2&gt;

&lt;p&gt;If our code uses async/await, we can test it easily with Mocha. Let’s rewrite the previous example from using promises to use async/await.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/QWdmXBb"&gt;QWdmXBb&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you can notice, we’ve added the &lt;strong&gt;async&lt;/strong&gt; keyword to the save method, and when calling the method we use the await to wait for the asynchronous call result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let result = await collection.save();

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

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Check out all posts in the series&lt;/em&gt; &lt;strong&gt;&lt;a href="https://www.danielwerner.dev/category/testing-with-mocha/"&gt;Testing with Mocha&lt;/a&gt;&lt;/strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/testing-with-mocha-3-testing-asynchronous-code/"&gt;Testing with Mocha – 3. Testing asynchronous code&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testingwithmocha</category>
      <category>bdd</category>
      <category>javascript</category>
      <category>mocha</category>
    </item>
    <item>
      <title>Testing with Mocha – 2. Using the assert library</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Tue, 13 Apr 2021 15:00:43 +0000</pubDate>
      <link>https://dev.to/daniel_werner/testing-with-mocha-2-using-the-assert-library-183m</link>
      <guid>https://dev.to/daniel_werner/testing-with-mocha-2-using-the-assert-library-183m</guid>
      <description>&lt;p&gt;In the previous lesson we’ve created our first test, but used only one assertion to check if the result equals to a predefined value. Let’s take a look at the capabilities of the &lt;a href="https://www.chaijs.com/"&gt;Chai&lt;/a&gt;’s assert library, and explore them through some more advanced examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using strictEqual and notStrictEqual
&lt;/h2&gt;

&lt;p&gt;In the first example we’ve already used the &lt;strong&gt;equal&lt;/strong&gt; assertion. The basic difference between equal and strictEqual is that the latter compares the types as well. The &lt;strong&gt;strictEqual&lt;/strong&gt; only accept the value if it’s type and value is the same as the expected one.&lt;/p&gt;

&lt;p&gt;Let’s see and example of how to use the &lt;strong&gt;strictEqual&lt;/strong&gt;. We’ll use the example from the previous lesson and assert that our sum method return the correct result, this time type checking. Also we add a new functionality to the sum method, if it encounters a non numeric item in the data, it returns null. We’ll also create a test for that.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/XWpEqjq"&gt;Testing with Mocha – Example 2.1&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes we may want to assert that the result of a method call does &lt;strong&gt;NOT&lt;/strong&gt; equal to a given value. In our example the sum method can return a number when the sum is successful or null if it is not. Let’s create an assertion that it never returns false.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/xxgWjgQ"&gt;Testing with Mocha – Example 2.2&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing objects
&lt;/h2&gt;

&lt;p&gt;We’ll continue to develop our collection class from the previous lesson, add more features to it and learn the other assertion types.&lt;/p&gt;

&lt;h3&gt;
  
  
  The assert.deepStrictEqual
&lt;/h3&gt;

&lt;p&gt;The equal assertion cannot be used on object, like our collection. For this purpose we can use the deep strict equal assertion. It checks the objects own enumerable properties, and strictly checks their types.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/XWpEqoo"&gt;Testing with Mocha – Example 2.3&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we saw in the previous example if the assertion fails when only the type differs in the data. Let’s see another use case for this. We’ll add a push method to our collection. The push method can be used to add new element to the collection.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/ExZELMJ"&gt;Testing with Mocha – Example 2.4&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we can see from the above example, we use the push method to add a new element to the collection, and use the deepStrictEqual to make push method works correctly, and the collection has the same elements as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let expectedCollection = new Collection([1,2,3,4])

              collection.push(4);
              assert.deepStrictEqual(collection, expectedCollection);

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

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Check out all posts in the series&lt;/em&gt; &lt;strong&gt;&lt;a href="https://www.danielwerner.dev/category/testing-with-mocha/"&gt;Testing with Mocha&lt;/a&gt;&lt;/strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/testing-with-mocha-2-using-the-assert-library/"&gt;Testing with Mocha – 2. Using the assert library&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testingwithmocha</category>
      <category>bdd</category>
      <category>javascript</category>
      <category>mocha</category>
    </item>
    <item>
      <title>Testing with Mocha – 1. Installation and set up</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Mon, 12 Apr 2021 08:25:36 +0000</pubDate>
      <link>https://dev.to/daniel_werner/testing-with-mocha-1-installation-and-set-up-23k5</link>
      <guid>https://dev.to/daniel_werner/testing-with-mocha-1-installation-and-set-up-23k5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Mocha can be run both from the command line or from the browser. If you want to run it from the terminal you can use npm or yarn to install it. You can install mocha globally and use it in all your projects or install as as dev dependency in the project locally. I’d recommend installing it locally because this way you can have different versions of Mocha in different projects, and also it makes it easier to install Mocha in continuous integration system to automate the test suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Mocha
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing via command line
&lt;/h3&gt;

&lt;p&gt;Let’s see how to install it globally using &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --global mocha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this method mocha is installed in the project’s &lt;code&gt;node_modules&lt;/code&gt; directory, and it’ll be accessible only in that specific project.&lt;/p&gt;

&lt;p&gt;If you use yarn, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add mocha
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing in the browser
&lt;/h3&gt;

&lt;p&gt;For running mocha in the browser you need to load &lt;code&gt;mocha.js&lt;/code&gt; and &lt;code&gt;mocha.css&lt;/code&gt; in your html. The advantage of using Mocha in browser is that you get a nice test report in the browser, and you don’t have to install via npm. But for me the huge disadvantage is that it cannot be automated. For example cannot be included in the build process. A typical html for running Mocha tests looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;title&amp;gt;Mocha Tests&amp;lt;/title&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&amp;gt;
    &amp;lt;link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id="mocha"&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;script src="https://unpkg.com/chai/chai.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="https://unpkg.com/mocha/mocha.js"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;script class="mocha-init"&amp;gt;
      mocha.setup('bdd');
      mocha.checkLeaks();
    &amp;lt;/script&amp;gt;
    &amp;lt;script src="test.array.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="test.object.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="test.xhr.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script class="mocha-exec"&amp;gt;
      mocha.run();
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating our first test
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How to run mocha
&lt;/h3&gt;

&lt;p&gt;In the previous lesson we installed Mocha, let’s create our first test.&lt;/p&gt;

&lt;p&gt;If we installed mocha globally, then we can run it simply by running:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;or if we installed locally, run it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
./node_modules/mocha/bin/mocha

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

&lt;/div&gt;



&lt;p&gt;By default mocha looks for the test files in the &lt;code&gt;test&lt;/code&gt; directory, so we can either create a &lt;code&gt;test&lt;/code&gt; directory in the project – and run the tests with the above commands -, or we can specify the pattern where to look for the test files, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
mocha ./*.js

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

&lt;/div&gt;



&lt;p&gt;The above pattern means look at the current directory, and run every .js file in this directory. There are other configuration options as well, we’ll check them out in the following lessons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our first test
&lt;/h3&gt;

&lt;p&gt;Let’s create our first test. We’ll start with creating a simple class called Collection to extend the capabilities of the built in JavaScript array. It accepts an array in the constructor and has a sum method to sum all the values of the array. Please find the executable code snippet below.&lt;/p&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/daniel-werner-the-decoder/pen/PoWQaRB"&gt;Testing with Mocha – First test&lt;/a&gt; by Daniel Werner (&lt;a href="https://codepen.io/daniel-werner-the-decoder"&gt;@daniel-werner-the-decoder&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s take a look what we have here in this test. In this example we use the node.js’ built in assertion module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
var assert = require('assert');

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

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;describe()&lt;/code&gt; function we can define the test suite, or we can say organize the tests in logical groups. You can nest the &lt;code&gt;describe()&lt;/code&gt; functions according to your needs, to have a nice and structured output, like we did in the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
describe('Collection', function () {
  describe('#sum()', function () {

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;it()&lt;/code&gt; function defines the actual test which should do the testing and the assertions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
it('should return 6 ', function () {

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

&lt;/div&gt;



&lt;p&gt;We do the assertion with the assert library, in this case asserting that the result equals to the expected value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
assert.equal(collection.sum(), 6);

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

&lt;/div&gt;



&lt;p&gt;Thats it! We created our first working test with Mocha! In the following lessons we’ll take a deeper look at how to configure mocha, how to create more complex tests and how to use different assertion libraries with Mocha.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Check out all posts in the series &lt;strong&gt;&lt;a href="https://www.danielwerner.dev/category/testing-with-mocha/"&gt;Testing with Mocha&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/testing-with-mocha-installation-and-set-up/"&gt;Testing with Mocha – 1. Installation and set up&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testingwithmocha</category>
      <category>bdd</category>
      <category>javascript</category>
      <category>mocha</category>
    </item>
    <item>
      <title>Under the hood: How database transactions work in Laravel</title>
      <dc:creator>Daniel Werner</dc:creator>
      <pubDate>Mon, 01 Feb 2021 17:00:00 +0000</pubDate>
      <link>https://dev.to/daniel_werner/under-the-hood-how-database-transactions-work-in-laravel-2b9l</link>
      <guid>https://dev.to/daniel_werner/under-the-hood-how-database-transactions-work-in-laravel-2b9l</guid>
      <description>&lt;p&gt;Continuing the under the hood series, this time about database transactions in Laravel. I won’t repeat all the things on how you can use the transactions in Laravel. If you are not familiar with the topic you can find everything in the documentation &lt;a href="https://laravel.com/docs/8.x/database#database-transactions"&gt;here&lt;/a&gt;. We’re now focusing on how these implementations work in the background, what caused us some headache lately and how to avoid it. So let’s dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transactions with closure
&lt;/h2&gt;

&lt;p&gt;As you are probably already familiar with it, you can use closures in the DB facade’s transaction method, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB::transaction(function () {
    DB::update('update users set votes = 1');
    DB::delete('delete from posts');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this method you don’t need to worry about starting the transaction, committing and rolling back if anything goes wrong, it does all of that automatically.&lt;/p&gt;

&lt;p&gt;The transaction related methods are located in the &lt;strong&gt;Illuminate/Database/Concerns/ManagesTransactions.php&lt;/strong&gt; trait. You can also pass a second argument to the DB::transaction method which is the number of attempts to try the transaction again when an exception or deadlock occur. The code loops through the given number of attempt and starts a new transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function transaction(Closure $callback, $attempts = 1)
{
    for ($currentAttempt = 1; $currentAttempt &amp;lt;= $attempts; $currentAttempt++) {
        $this-&amp;gt;beginTransaction();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will try to execute the callback function, and if any exception occurs in the callback, rolls back the transaction. It will try again until it reaches the defined number of attempts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try {
    $callbackResult = $callback($this);
}

// If we catch an exception we'll rollback this transaction and try again if we
// are not out of attempts. If we are out of attempts we will just throw the
// exception back out and let the developer handle an uncaught exceptions.
catch (Throwable $e) {
    $this-&amp;gt;handleTransactionException(
        $e, $currentAttempt, $attempts
    );

    continue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went fine commits the transaction,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try {
    if ($this-&amp;gt;transactions == 1) {
        $this-&amp;gt;getPdo()-&amp;gt;commit();

        optional($this-&amp;gt;transactionsManager)-&amp;gt;commit($this-&amp;gt;getName());
    }

    $this-&amp;gt;transactions = max(0, $this-&amp;gt;transactions - 1);
} catch (Throwable $e) {
    $this-&amp;gt;handleCommitTransactionException(
        $e, $currentAttempt, $attempts
    );

    continue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;returns the callback result and fires a committed event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$this-&amp;gt;fireConnectionEvent('committed');

return $callbackResult;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using closures is the simplest and easiest way to use transactions in Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual transaction handling
&lt;/h2&gt;

&lt;p&gt;Laravel allows you to control the transaction yourselves. And that is what caused us problems lately. We’ll come to that point soon, but first let’s see how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a transaction with &lt;strong&gt;DB::beginTransaction();&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If everything went well commit using &lt;strong&gt;DB::commit();&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Or rollback otherwise with &lt;strong&gt;DB::rollBack();&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works just fine, but what if you’d like to use nested transactions. MySQL for example doesn’t support &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html"&gt;nested transactions&lt;/a&gt;, but the InnoDB engine supports &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/savepoint.html"&gt;savepoints&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Laravel makes use of this feature (if supported). So when we call &lt;strong&gt;DB::beginTransaction()&lt;/strong&gt; it counts the nesting level of transactions. According to this nesting level it either creates a new transaction (first time), or creates a savepoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if ($this-&amp;gt;transactions == 0) {
    $this-&amp;gt;reconnectIfMissingConnection();

    try {
        $this-&amp;gt;getPdo()-&amp;gt;beginTransaction();
    } catch (Throwable $e) {
        $this-&amp;gt;handleBeginTransactionException($e);
    }
} elseif ($this-&amp;gt;transactions &amp;gt;= 1 &amp;amp;&amp;amp; $this-&amp;gt;queryGrammar-&amp;gt;supportsSavepoints()) {
    $this-&amp;gt;createSavepoint();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In situations when the nesting level is more than one, calling the &lt;strong&gt;DB::rollBack()&lt;/strong&gt; will decrement the counter, and roll back to the previous savepoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected function performRollBack($toLevel)
{
    if ($toLevel == 0) {
        $this-&amp;gt;getPdo()-&amp;gt;rollBack();
    } elseif ($this-&amp;gt;queryGrammar-&amp;gt;supportsSavepoints()) {
        $this-&amp;gt;getPdo()-&amp;gt;exec(
            $this-&amp;gt;queryGrammar-&amp;gt;compileSavepointRollBack('trans'.($toLevel + 1))
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here comes the tricky part: the &lt;strong&gt;DB::commit()&lt;/strong&gt; will only really commit to the database when the counter equals to 1. Otherwise it only decrements the counter and fires the committed event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function commit()
{
    if ($this-&amp;gt;transactions == 1) {
        $this-&amp;gt;getPdo()-&amp;gt;commit();

        optional($this-&amp;gt;transactionsManager)-&amp;gt;commit($this-&amp;gt;getName());
    }

    $this-&amp;gt;transactions = max(0, $this-&amp;gt;transactions - 1);

    $this-&amp;gt;fireConnectionEvent('committed');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously this is fine, but you need make sure that for every started transaction you call either commit or rollback. Especially when doing it in a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example when things go wrong
&lt;/h2&gt;

&lt;p&gt;So here is an example, what caused headache in our case (it is pseudo code, but the essence is the same):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$products = Products::all()
foreach($products as $product){
    try { 
        DB::beginTransaction();

        if ($product-&amp;gt;isExpensiveEnough()) {
            continue;
        }

        $product-&amp;gt;price += 100;
        $product-&amp;gt;save();
        DB::commit();
    }
    catch(Exception $e){
        DB::rollback();    
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So basically we want to loop through the products and change some attributes according to some conditions. In this pseudo code we want to raise the price for the products which are not expensive enough :-).&lt;/p&gt;

&lt;p&gt;So what is the problem with the above example? In case when we update the product and everything goes well, we begin a transaction, update the model, commit. It even works well when an exception occurs, and we do a rollback.&lt;/p&gt;

&lt;p&gt;However when the conditions are not fulfilled, and we &lt;strong&gt;continue&lt;/strong&gt; , in the next iteration a new transaction gets created. The transaction counter will be increased to 2. The following commit &lt;strong&gt;would not do&lt;/strong&gt; an actual commit to the database, just decrement the counter. The next iteration would begin a new transaction increase the counter to 2 etc. Once the number of beginTransaction() and commit()/rollback() method get’s “out of sync” the following iterations won’t update the records.&lt;/p&gt;

&lt;p&gt;It is not funny when it is a command that runs for couple of hours, and at the end doesn’t update anything it should.&lt;/p&gt;

&lt;p&gt;So the lesson we learned here is simple. Always keep in mind that every opened transaction should be concluded with a commit or rollback. Especially in loops.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.danielwerner.dev/under-the-hood-how-database-transactions-work-in-laravel/"&gt;Under the hood: How database transactions work in Laravel&lt;/a&gt; appeared first on &lt;a href="https://www.danielwerner.dev"&gt;Daniel Werner&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>underthehood</category>
      <category>databasetransactions</category>
    </item>
  </channel>
</rss>
