Articles

May 6, 2022

Tips for developing with VueJS, InertiaJS, Laravel and TailwindCSS (VILT) Stack

VueJS, InertiaJS, Laravel, TailwindCSS - more commonly known as **VILT** stack is one of the modern approaches for developing web apps with monolithic architecture. In this post, I want to share with you some tips based on my personal experience developing with VILT stack. # Inertia Version When you install InertiaJS for Laravel, the process will need you to publish the middleware `HandleInertiaRequests` within your web middleware group. In this class is a public method `version` that is used by Inertia to determine the current version of your assets. However, this particular method has some issues when you are running tests that are doing json assertions. What I recommend is to disable this when running tests, like so... ``` // app/Http/Middleware/HandleInertiaRequests.php public function version(Request $request): ?string { return (! app()->runningUnitTests()) ? parent::version($request) : null; } ``` # The Inertia Testing Header [InertiaJS comes with its own tools and approach for writing tests and performing assertions.](https://inertiajs.com/testing) However, for endpoint tests, if you still want to use the traditional response assertions from Laravel, you can pass `['X-Inertia' => 'true']` as a header for each request like so... ``` public function can_get_user_edit_page() { $user = User::factory()->create(); $response = $this->get( route('admin.user.edit', $user), ['X-Inertia' => 'true'] // Set this... ); $response->assertJsonFragment(['component' => 'Admin/User/Edit']) ->assertJsonFragment(['name' => $user->name]); } ``` # When Login is not an Inertia page When developing an app that has a login page that is not part of Inertia pages, you will encounter this particular gotcha. When the authentication of your user expires, you will see your login page displayed on the modal like this: ![login-page-on-modal](https://marvinquezon.com/storage/uploads/login-page-on-modal.png) This would normally occur if the user left the app open and idle for quite sometime and their session expired, then they tried to navigate on an Inertia page. Manually refreshing the page would do the trick. But if the user tries to login through the form that is on the modal and is successful, it would then render the page on the modal. Pretty nasty UI issue. You can easily replicate this by logging in a user, delete the session then navigate to an Inertia page. Luckily, this can easily be solved by just overriding the `unauthenticated` method in your `App\Exceptions\Handler` class, and making it so that Inertia handles the redirection to the login page for HTTP response like so... ``` // app/Exceptions/Handler.php use Inertia\Inertia; class Handler extends ExceptionHandler { //... protected function unauthenticated($request, AuthenticationException $exception) { // Check first if request is an inertia request. // And if so, we redirect to the login page... if ($request->hasHeader('X-Inertia', true)) { return Inertia::location(route('login')); } if ($this->shouldReturnJson($request, $exception)) { return response()->json(['message' => $exception->getMessage()], 401) } return redirect()->guest($exception->redirectTo() ?? route('login') } } ``` # Let TailwindCSS scan your PHP files InertiaJS was made for building modern monolithic apps. This means coupling your frontend with your backend much like when building using Blade views. So there are times that some frontend entities like classes can come from the backend. For one, I like using Enums for determining status of certain models. The Enums would also hold some color values that frontend can render. With that in mind, if you're using TailwindCSS as your frontend CSS framework, you might want to allow it to scan your PHP files so it can detect classes that are stored at the backend. You can do this by modifying your `tailwind.config.js` file and adding the app path within the content array, like so... ``` module.exports = { content: [ './resources/**/*.js', './resources/**/*.vue', './app/**/*.php', ], } ``` # Inertia View Composer Package This one would sound like a shameless plug. But one really powerful feature that InertiaJS has not yet implemented as of this writing is the ability to be able to share data to the frontend based on the page name. Laravel Blade views has this and its called [**View Composers**](https://laravel.com/docs/9.x/views#view-composers). So you might want to check out this package that I created called [Kinetic](https://github.com/ambengers/kinetic) that does exactly that - share data to your frontend based on the Inertia page name. I've personally used this in one of the latest projects that I am working on and it helped me alot. Happy coding!

April 30, 2022

Practical Tips for Writing Beautiful PHP Code

Here are some straight-forward and practical tips on how to make your PHP code beautiful. # Give it some space As romantically-cliche as it may sound, sometimes all your code needs is a bit of space. A little bit of breathing room so you and your team can easily read it better. And as basic as it may seem, many, both new and experienced developers tend to still forget the idea of adding spaces on their code. Take a look at these code blocks below... ``` public function test_can_upload_avatar() { $this->user = User::factory()->create(); $avatar = UploadedFile::fake()->image($filename = 'avatar.jpeg'); $this->post(route('user.profile.avatar'), ['avatar' => $avatar]); $this->assertDatabaseHas('media', [ 'model_type' => $this->user->getMorphClass(), 'model_id' => $this->user->id, 'file_name' => $avatar->hashName(), ]); $this->assertNotNull($this->user->avatar->first()); $this->assertInstanceOf(Media::class, $this->user->avatar->first()); } ``` ``` public function test_can_upload_avatar() { $this->user = User::factory()->create(); $avatar = UploadedFile::fake()->image($filename = 'avatar.jpeg'); $this->post(route('user.profile.avatar'), ['avatar' => $avatar]); $this->assertDatabaseHas('media', [ 'model_type' => $this->user->getMorphClass(), 'model_id' => $this->user->id, 'file_name' => $avatar->hashName(), ]); $this->assertNotNull($this->user->avatar->first()); $this->assertInstanceOf(Media::class, $this->user->avatar->first()); } ``` They are obviously the same piece of code. I don't know about you, but just eye-balling these code blocks, I definitely find the latter much easier to take in and understand because of its ample spacing. Like, I don't feel the need to bring my face closer to the screen of my laptop just to be able to understand what each line of code does, if you know what I mean. As a rule of thumb, try to put each statement (anything that ends with a semi-colon) on its own line unless 2 or more lines are doing the same type of action (i.e. the assertions on the example above, assigning variables, etc.). # Use PHP CS Fixer One advantage PHP has over other programming languages is that we have our own set of coding standards that developers generally agree upon. The [PHP Standards Recommendation or PSR](https://www.php-fig.org/psr/) are set of recommendations for styling our code. From **Basic Coding Standard**, **Coding Style** and **Autoloading Standards** the PSR has you covered so you don't have to invent your own. Another good thing is that you can automate the implementation of these standards on your IDE using the [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) tool. Modern IDEs like Sublime Text, PhpStorm and VS Code have their own packages that you can just download from their respective package managers to set this up. All you need to do is to configure the set of rules that may want to implement for you or your team. If you have no idea on what rules to implement, here's a link to a [gist of set of rules that I personally use in my projects](https://gist.github.com/ambengers/c112d75bb7e14b41daf99dc2abad7690). I have mine set to auto run whenever I save the PHP file so that I am sure that these standards will always apply no matter what. You can also refer to this [PHP-CS-Fixer Cheat Sheet](https://mlocati.github.io/php-cs-fixer-configurator/#version:3.8) if you want to check on the meaning and effect of each rule. # Leave Meaningful Comments Another very basic yet many-tend-to-forget way of beautifying their code and helping themselves and/or their teammates understand their code better is by using comments. Not just comments, meaningful comments. Comments that actually makes sense when we you read it. I personally believe that writing meaningful comments actually reflects how much care a developer have put into their code. This is actually one reason why Laravel framework is so popular. And like-minded developers tend to gravitate towards it, developers who actually care for their code. Personally, as a rule of thumb, I leave comments on areas that I feel like, **if I don't remember the full context of how the code works, it will be quite hard for me to understand it again if I read it 6 months from the time that I wrote it**. I also try to follow Laravel's way of writing comments - where each line is a couple of characters shorter the the one above it.

April 24, 2022

Backed Enums with Collection - Part 2: Transforming to Array and Appending Attributes

In this post I will show how we can transform enums to array format and dynamically append attributes to it. Disclaimer: This article is a continuation to the [Supercharged Backend Enums with Collection](https://marvinquezon.com/posts/supercharged-backed-enums-with-collection) post that I've previously published. If you haven't already, I would highly recommend reading through that article first as we will continue with the examples from there. Now that that's out of the way, let's begin. ***Enums, as objects, are plain and boring!*** Enums as objects are plain and boring - because they can never hold other properties except `name` and `value` of each case. PHP will actually throw an error when you try to declare properties within enum classes. However, there are times that you would want to append additional properties to an enum case. For instance, going back to our `TicketStatus` enum class, imagine that for each status, we also want them to have a `color` attribute. So that whenever our frontend consumes it, we can use it to design maybe the background color of the tickets based on the status. This is very useful since the enum class is becoming the single source of truth for the ticket status. # Create a method and match each case First, let's see how we can assign a color for each enum case: ```

April 18, 2022

Supercharged Backed Enums with Collection

If you've been using PHP 8.1, chances are you already know about this new feature called [Enumerations or "Enums"](https://www.php.net/manual/en/language.enumerations.php). If not, then I suggest that you check it out as it is, in my opinion, one of the most powerful new feature PHP has added to the language. It might solve a lot of your use-cases as it did for me. As an example, let's say that we have a backed enum class that holds the cases for the status of the ticket in our app: ``` enum TicketStatus : string { case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; } ``` One cool thing about enumerations is that you can call a `cases()` method on it to list all the cases like so: ``` $result = TicketStatus::cases(); dd($result); [ TicketStatus { +name: "Draft", +value: "draft", }, TicketStatus { +name: "Open", +value: "open", }, TicketStatus { +name: "Ongoing", +value: "ongoing", }, TicketStatus { +name: "Closed", +value: "closed", }, ] ``` As you can see, each element in the result is an instance of `TicketStatus` class with properties `name` and `value`. So it begs the question, what if I just want to get just the names or just the values? Unfortunately, this isn't something that comes out of the box and calling `TicketStatus::names()` or `TicketStatus::values()` would cause an error. One solution is to use `Illuminate\Support\Collection` class. ``` $names = collect(TicketStatus::cases())->pluck('name'); dd($names); Illuminate\Support\Collection { all: [ "Draft", "Open", "Ongoing", "Closed", ], } $values = collect(TicketStatus::cases())->pluck('values'); dd($values); Illuminate\Support\Collection { all: [ "draft", "open", "ongoing", "closed", ], } ``` Okay, now we're talking! Let's refactor that as methods in our enum class, like so: ``` enum TicketStatus : string { case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; public static function names() { return collect(self::cases())->pluck('name'); } public static function values() { return collect(self::cases())->pluck('value'); } } ``` Now, calling `TicketStatus::names()` or `TicketStatus::values()` would definitely work! But wait, there's more! Since we are already using `Collection` class here, we might as well see to it that we are able to take advantage of the full features it can offer. How about refactoring to have something like `TicketStatus::collection()` ? Let's do it! ``` enum TicketStatus : string { case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; public static function names() { return self::collection()->pluck('name'); } public static function values() { return self::collection()->pluck('value'); } public static function collection() { return collect(self::cases()); } } ``` Cool! Now we can take full advantage of collection features within our enum. In the future we can do something like `TicketStatus::collection()->map(...)` or `TicketStatus::collection()->each(...)` should we need to. It is now up to you if you want to add more convenience methods or just chain from the `collection()` method. Last thing that we would want to do is to be able to share these convenient functions with other enum classes. So let's wrap it within a trait... ``` trait Collectible { public static function names() { return self::collection()->pluck('name'); } public static function values() { return self::collection()->pluck('value'); } public static function collection() { return collect(self::cases()); } } ``` And just use the trait within your enum classes like so ``` enum TicketStatus : string { use Collectible; case Draft = 'draft'; case Open = 'open'; case Ongoing = 'ongoing'; case Closed = 'closed'; } ``` And there you have it! You have supercharged your backed enum classes using collection. Hope you enjoy!

April 12, 2022

Assertable Json String (Laravel 8+)

Once in a while when diving deep into the Laravel codebase, I find that there are hidden gems that I think would really be helpful to developers. In this post, I'll shed light on one of those hidden gems. I'm talking about the `AssertableJsonString::class` that is new since Laravel version 8.x. Let's go! Typically when testing json responses, we have the ability to chain json assertions from the response like so: ``` public function test_json_response() { $response = $this->get(route('some.json.endpoint')); $response->assertJson(['foo' => 'bar']) ->assertJsonMissing(['bar' => 'baz']); } ``` Thanks to the fact that response is an instance of `Illuminate\Testing\TestResponse::class` which holds these convenient json assertion methods. However, there are **cases that we need to make assertions against json data that isn't coming from a json endpoint**. One example is reading a json file: ``` public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); // This won't work since $data is just a plain array and // not an instance of Illuminate\Testing\TestResponse $data->assertJsonFragment(["vue" => "^3.2.31"]); } ``` Our typical assertions will not work on this example since `$data` is just a plain array. Before Laravel 8.x my workaround for this is to either pull in a third-party package specifically for json assertions, or wrap the json data with `Illuminate\Testing\TestResponse::class` like so: ``` use Illuminate\Http\Response; use Illuminate\Testing\TestResponse; ... public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); // Wrap the data with Illuminate\Testing\TestResponse class // so that you can perform your typical json assertions... $response = TestResponse::fromBaseResponse(new Response($data)); $response->assertJsonFragment(["vue" => "^3.2.31"]); } ``` This approach works, but feels kind of hacky. For one, the data is not really an HTTP response so why does it have to go through these response classes? Thankfully, Laravel 8.x extracted these json assertions into a class called `AssertableJsonString::class`! Now we can just wrap our json data with this class and perform our assertions like so: ``` use Illuminate\Testing\AssertableJsonString ... public function test_read_package_json_data() { $data = json_decode( file_get_contents(base_path('package.json')), true ); $data = new AssertableJsonString($data); $data->assertFragment(["vue" => "^3.2.31"]) ->assertMissing(["php" => "^8.1" ]); } ``` Enjoy coding!

April 11, 2022

Dear recruiters - please don't do this..

So, I have recently resigned from the company that I work for and started looking for a new one. I sent out my resume to a few interesting senior PHP developer recruitment ads that I saw online and one recruiter scheduled me for an initial interview. The interview went well and I was able to answer all of the questions she asked me. I was also able to ask couple of questions myself. So she scheduled me for a technical exam which she will send the details through the email. Couple of hours later I received the email with the instructions for the technical exam. I was drinking soda while reading the email and I almost choked when I got to this part: > Create the following: > > A web app that does CRUD with authentication and user roles using PHP/Laravel + React.JS. > Create APIs, using PHP/Laravel, that is consumed by your mobile app (React Native, iOS preferably otherwise Android). > Add features that will impress us to showcase your skills, expertise, and experience. Examples below: > > - payment gateway (Paypal, preferably Stripe) > - maps / navigation (Google Maps, Here, etc.) > - social media (SSO, automatic posting, etc.) > - machine learning / AI (facial recognition, eye tracking, etc.) > - blockchain / cryptocurrency > - digital signature, image processing, etc. > - SMS / email integration > - real-time, push notification (chat for example) > - may use any other external or 3rd party API integrations My first thoughts were, *Are they serious?! Are they really asking me to implement Payment Gateway, Navigation, Machine Learning/AI, Blockchain/Cryptocurrency on a freaking technical exam to IMPRESS THEM?!* Dear recruiters, please do not do this. This is equivalent to me applying as a dentist and you're asking me to perform a brain surgery as a technical exam just so I could impress you. This is like asking me to develop a product MVP on my technical exam. This is giving me an impression that you either do not know what you're looking for from a senior developer, or you have no idea what these technologies are. If you have no idea what skillsets to look for from a candidate, then ask some one who has expertise on the field, like other senior developers. And I am 100% sure they will not include Blockchain or Machine Learning from that list.

March 31, 2022

DRY, KISS and YAGNI vs Coding Standards

In this post, I am going to talk about my personal opinion regarding DRY, KISS and YAGNI versus Coding Standards especially when working in a team setting. I understand that you might not agree on some (or maybe even all) of the things that I have written here and I respect that. Everyone is entitled to their own opinion. So I encourage you all my readers, to look at this article with an objective perspective and a broader-than-usual mindset. So with the disclosure out of the way, let me get to the point. Always prioritize DRY, KISS and YAGNI over Coding Standards - even if you are in a team setting. This comes with one condition however, that you need to have a good amount of test coverage so you have confidence in case that you need to refactor your code in the future. **DRY *(Don't Repeat Yourself)*** - means that each small pieces of knowledge (code) may only occur exactly once in the entire system **KISS *(Keep It Simple, Stupid!)*** - means to try to keep each small piece of software simple and unnecessary complexity should be avoided to not incur any technical debt. **YAGNI *(You Ain't Gonna Need It!)*** - means that always implement things when you actually need them and never implement things before you need them. I personally think these principles trump any coding standards a team might implement. And I am willing to go above and beyond that and say **DRY, KISS and YAGNI should be the guiding principles of teams when implementing Coding Standards**. ## Story Time! I have recently started working as a Lead Developer in a team, and one of their Coding Standard is to **always use a FormRequest class** in the controller. So here I am, working on a feature and I have one endpoint that only receives one parameter on the request, which is already covered with written tests. Also, mind you that this request parameter is only used for this endpoint and not anywhere else in the application. ``` public function update(Request $request) { $request->validate(['processed' => 'required|boolean' ]); // Some other process here then return a json response... } ``` Now, I am being told to refactor this into a `FormRequest` class. So I asked, why? Because I honestly don't see the need to. Why do I have to incur additional tech debt by adding a separate class that will only be used for validating a single request attribute? And this piece is not going to be used in any other place,so i'm not violating code duplication. What gives? KISS and YAGNI! ***But we have to follow Coding Standards!*** This is a classic example of letting the developers incur additional techical debt without any benefit at all, just for the sake of Coding Standards. In my most honest opinion, when the code is well-tested, readable and at its simplest form; when it can be easily changed when the need arise, then that is a good code. And I understand the need to be one with the team you are working with, but programming is not a military industry but a creative one. And the possible amount of incurred technical debt when blindly following coding standards far outweighs that argument. And most of the time, that incurred technical debt is never paid. I have seen it multiple times. Also, developers grow when they get to figure out their ways of deciding when to use certain methodologies and tools, and when to keep things simple. That decision can then be guided by the team during a code review session. And again, as long as the code is covered with tests, refactoring should never be a big concern.

Marvin Quezon · Copyright © 2025 · Privacy · Sitemap