DEV Community

Cover image for Tip for Using incrementEach in Laravel
Ivan Mykhavko
Ivan Mykhavko

Posted on

2

Tip for Using incrementEach in Laravel

Laravel provides a convenient way to increment or decrement column values in a model using the increment and decrement methods.

Starting from Laravel 9, an additional method, incrementEach (and decrementEach), allows updating multiple columns at once.

The Problem: Incrementing and Decrementing Simultaneously

What if you need to increment and decrement different columns at the same time?

Consider the following scenario with a PriceImport model that has the columns imported_rows and unknown_rows. You might try something like this:

PriceImport::query()->increment('unknown_rows')->decrement('imported_rows');
Enter fullscreen mode Exit fullscreen mode

But, this approach won't work since increment and decrement methods can't be chained.

Common Workaround (But Not Ideal)

Typical way to handle this is by using DB::raw():

PriceImport::query()->whereKey($priceImport->id)->update([
    'imported_rows' => DB::raw('imported_rows + 1'),
    'unknown_rows' => DB::raw('unknown_rows - 1'),
]);
Enter fullscreen mode Exit fullscreen mode

Or little better:

PriceImport::query()->whereKey($priceImport->id)->increment('imported_rows', [
    'unknown_rows' => DB::raw('unknown_rows - 1'),
]);
Enter fullscreen mode Exit fullscreen mode

While this works, it's not the most elegant or laravel-friendly solution.

The Elegant Solution: Using Negative Values with incrementEach

Laravel’s incrementEach method allows passing negative values, which effectively works as a decrement operation.

Here’s how you can achieve both incrementing and decrementing at the same time in a clean way:

Full Example

<?php

namespace App\Actions\PriceImport;

use App\Actions\Actionable;
use App\Models\PriceImport\PriceImport;
use App\Models\PriceImport\PriceImportProduct;
use App\QueryBuilder\Queries\PriceImport\MovePriceImportDuplicateRowQuery;

final class PriceImportHandleDuplicateAction implements Actionable
{
    public function handle(PriceImport $priceImport): void
    {
        $duplicateCount = PriceImportProduct::query()
            ->where('row_occurrences', '>', 1)
            ->where('price_import_id', $priceImport->id)
            ->count();

        PriceImport::query()->whereKey($priceImport->id)->incrementEach([
            'imported_rows' => -$duplicateCount, // Decrementing
            'unknown_rows' => $duplicateCount,   // Incrementing
        ]);

        (new MovePriceImportDuplicateRowQuery($priceImport->id))->query();
    }
}
Enter fullscreen mode Exit fullscreen mode

Resulting Query

UPDATE `price_imports`
SET `imported_rows` = `imported_rows` + -1,
    `unknown_rows` = `unknown_rows` + 1
WHERE `price_imports`.`id` = 5;
Enter fullscreen mode Exit fullscreen mode

Conclusion

With this simple trick, you can increment and decrement multiple columns simultaneously using Laravel’s incrementEach method, making your code cleaner and more readable.

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (6)

Collapse
 
xwero profile image
david duymelinck • Edited

It doesn't make the code more readable, because at first sight the code increments the columns. Even with the comment, the minus symbol was hard to spot.

I don't understand what is wrong with the update method.
If using DB::raw is not your thing you can write a trait.

trait DbRaw {
    protected function increment(string $column, int $amount = 1)
   {
        return DB::Raw("$column + $amount");
   }

   protected function decrement(string $column, int $amount = 1)
   {
        return DB::Raw("$column - $amount");
   }
}

// repository.php
PriceImport::query()->whereKey($priceImport->id)->update([
    'imported_rows' => $this->increment('imported_rows'),
    'unknown_rows' => $this->decrement('unknown_rows'),
]);
Enter fullscreen mode Exit fullscreen mode

You could even create collection macros that fills a mass assignment array.

Collection::macro('increment', function ($column, $amount = 1) {
    return $this->put($column, DB::raw("$column + $amount"));
});

Collection::macro('decrement', function ($column, $amount = 1) {
    return $this->put($column, DB::raw("$column - $amount"));
});

// repository
PriceImport::query()->whereKey($priceImport->id)->update(
  collect()->increment('imported_rows')->decrement('unknown_rows')->all()
);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
tegos profile image
Ivan Mykhavko

Thank you for your feedback and for sharing your code examples!
I see your point about readability it might not be immediately clear that the value is being decremented rather than incremented.
I'll consider making that aspect more explicit in future examples.

I just want demonstrate that incrementing can be done this way.
I dont have anything against DB::raw, you're free to use it wherever you prefer or even not use eloquent at all. 😊

I appreciate your suggestion about using a trait or collection macros for handling DB::raw operations.
However, I personally don't use traits or macros, as I don’t consider them the best architectural choices.

Collapse
 
xwero profile image
david duymelinck

I added the collection example because Laravel developers love a fluent API.
I wouldn't use it either if I had the choice.

What is stopping you from using traits?

Thread Thread
 
tegos profile image
Ivan Mykhavko

Simply, I don't like traits. First, the term itself doesn't exist in oop, and using traits can prevent certain oop principles from being fully applied.
Second, their implementation is working like a copy-paste mechanism.
When I see a codebase where everything revolves around traits, it usually indicates poor design.
Here are some thoughts on traits:
barryosull.com/blog/why-i-don-t-li...

Thread Thread
 
xwero profile image
david duymelinck

I never thought much about it because the same mechanism exists in other languages too. Even in Java you can create methods with a body in interfaces. Then I think the PHP implementation is a bit cleaner.

If multiple languages have this mechanism to overcome the single class inheritance, that means that there is enough friction in codebases not to consider the composition alternative.

I share your concerns, but I'm not going out of my way to avoid them.

Thread Thread
 
tegos profile image
Ivan Mykhavko

I like your point of view. But in Java, this was added recently, in 2014, and I'm not sure if default methods are the same as PHP traits. It's interesting to chat with you. :)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more