Development

Is PHP dead? NO it’s not…

Development

The guys at OfferZen Origins used the footage that didn’t make the documentary on Laravel that was released last month, to create this video on PHP.

So all their memories are of, “Oh, here’s how PHP existed in 2005” or something like that.

Jeffrey Way

Share this post

Mailgun “On behalf of” issue

Development

We all know the feeling when we release a new project, we are overexcited until the first email goes out…
All of a sudden a client informs you, “Hey, the email I received has a strange “from” address. Can you fix it?“…

What is happening?

Even though we’ve registered our domain with Mailgun, made sure the correct DNS records are in place. And in our application, we’re using the correct API key combined with the domain and optionally the specific server that we prefer.

The “from” address in the email appears to be info=example.com@mg.example.com on behalf of info@example.com. which isn’t the address we wanted it to be, ideally it should be info@example.com

Mail::send(['html' => 'emails.template'], $data, function($message) use ($subject, $user){
    $message->to($user->email, $user->name);
    $message->subject("$subject");
    $message->from("info@example.com");
});

This issue is caused by the fact that we are using another domain, a subdomain, to configure Mailgun. This is advised, otherwise, we would endanger the normal mail functionality of the domain, if there are any in place, such as Google Workplace or Office 365.

It is the mismatch between the domain used to send our email (mg.example.com) and the domain indicated as the “from” address in our app (example.com).

Mailgun’s suggestion

Mailgun suggests a solution on its website. You can find it here.
The suggested solution may endanger the normal mail functionality as described above. Because we would overwrite the SPF record on the main domain. Which isn’t ideal.

A quick and easy solution

In Laravel, we can specify the sending domain in the email headers. For this, we can use the ->sender attribute.

Mail::send(['html' => 'emails.template'], $data, function($message) use ($subject, $user){
    $message->to($user->email, $user->name);
    $message->subject("$subject");
    $message->from("info@example.com");
    $message->sender("info@example.com");
});

By doing so we enforce the email sending address and the visible “from” address to be the same.

There, the job is done. Happy customer.

Share this post

7 quick Laravel Tips

Development

Here are 7 quick tips for your next Laravel project.

Based on experience I’ve Tweeted some of these tips and though, why don’t I gather them into a blogpost. I hope these come in handy when writing your applications, in any case I use them quite often.

1. Eager Loading

When loading model relationships we could use eager loading in order to reduce the amount of queries that are performed while fetching data.
Only fetching those relation columns that are actually needed is better them pulling them all in at once.

$cars = Car::with('brand:id,name')->get();

We could also eager load a relationship by default by adding it to the model in a protected "$with" variable. By doing so the specified relationship will eagerly loaded during a regular fetch.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Car extends Model {
    /**
* The relationships that should always be loaded.
*/
    protected $with = ['brand'];

    /**
* Get the brand that builds the car.
*/
    public function brand() {
        return $this->belongsTo(Brand::class);
    }
}

2. Sharing data across views

if you want to share some variables across all views in a controller you could add use View::share in the constructor of the controller.
By doing so you can use the variable in all the views generator based from this controller.

namespace App\Http\Controllers;

use Illuminate\Support\Facades\View;

class CarController extends Controller
{
    /**
*  The variable $sharedData will be accessible
*  in both the index.blade.php and detail.blade.php.
*/
    public function __construct(){
        View::share('sharedData',['foo' => 'bar']);
    }

    public function index(){   
        return view('index');
    }

    public function detail(){   
        return view('detail');
    }
}

3. Route restrictions

Restricting route variables can be of use to add some extra security to your routes or split routes with numeric variables from those with alphanumeric variables.
For convenience reasons you can now use “WhereNumber, WhereAlpha, …” in stead of a regex style route restrictions.

/**
*   In Laravel vou can now use expressive Route restrictions ✨
*   Such as: WhereNumber, whereAlpha, whereUuid,
*   whereAlphaNumeric, ...
*/

// ❌ without restrictions
Route::get('/user/fid}', function($id) {
    //
});

// ❌ with a regex style check
Route::get('/user/{id}', function($id){
    //
})->where('id', '[0-9]+');

// ✅ Only numbers allowed
Route::get('/user/{id}', function($id) {
    //
})->whereNumber('id');

// ✅ Only alpha allowed
Route::get('/user/{name}', function ($id) {
    //
})->whereAlpha('name'):

4. Fetching Models without a relation

Fetching data based on the absence on a relation make easy with the doenstHave method. For example fetching all Orders that don’t have an Invoice yet.

/**
*   Fetch models that don't have a certain relationship
*   For example: Get all orders without an invoice.
*/

$orders = Order::doesntHave('invoice')->get();

/**
*   You can also chain "take" to this, F.E. take only 10
*/

$orders = Order::doesntHave('invoice')->take(10)->get():

5. Copying models correctly

Duplicating models, can be a mess in your code. Especially when the model has a lot of items there is a risk that one of the columns isn’t copied due to an human error. This can be prevented by using the replicate method that comes with Laravel for free.

/**
*   Copying models done correctly.
*   Using the replicate function.
*/

// ❌ How you could do it.
$newCar = new Car();
$newCar->model = $car->model;
$newCar->brand = $car->brand;
$newCar->catalogPrice = $car->catalogPrice;
$newCar->hp = $car->hp;
// ...
$newCar->save();

// ✅ How you should do it.
$newCar = $car->replicate();
$newCar->save();

6. Saving data quietly

Sometimes we need to update some data on a model, just some small things.
But in those cases we don’t want to trigger any Model Events, if there are any such as notifications or other automated processes.

We can save all our changes to a model without triggering those events by using the saveQuietly method. This way our updates will be stored quietly, as the name of the method indicates, thus without triggering any events.

/**
*   Updating model data without triggering
*   the Model Events? Save them Quietly. 🤫
*/

$user = User::find($id);
$user->firstname = "John";
$user->saveQuietly();

7. Fallback route

When a user visits a route that doesn’t exist the will be a 404 error code.
We could actually prevent the default 404 error page from rendering and replace it with our own, or provide some custom logic that we want to perform whenever this happens.

/**
*   Show a custom page when a user runs into a 404,
*   instead of throwing the regular 404.
*   With or without custom logic to spice it up. 🤷‍♂️
*/

Route::fallback(function(){
    // Your custom 404 logic or custom view goes here
});

Share this post

Custom Commands & Scheduled Tasks in Laravel

Development

After building a quite large application that was all about automating several tasks during the day, week and month I thought why don’t I create a tutorial covering this subject.

In this tutorial we go over the the basics of a custom command and level up the game with scheduling this custom command into a task, that runs at certain moments in time.

Share this post

What if a scheduled task fails

Development

Sometimes scheduled tasks fail. It’s happening more often than we think.
By adding “emailOutputOnFailure” to your scheduled task you’ll be notified with the output when it fails.

Additionally I like to add “withoutOverlapping” to it, just in case the task takes longer than expected.

I’ve also tweeted about this:

Share this post