Repository: laravel/framework
Pull request #54976: [12.x] Add `increment` and `decrement` methods to `Context`
Author: login: mattmcdev
Created: 2025-03-11T10:34:32Z | Merged: 2025-03-12T14:17:37Z

This PR proposes adding `increment` and `decrement` methods to `Context`.

This simplifies keeping track of a counter, e.g. number of records added, number of requests made, etc.


Diff:
diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php
index 1582d375e681..01e861925020 100644
--- a/src/Illuminate/Log/Context/Repository.php
+++ b/src/Illuminate/Log/Context/Repository.php
@@ -369,6 +369,35 @@ public function popHidden($key)
         return array_pop($this->hidden[$key]);
     }
 
+    /**
+     * Increment a context counter.
+     *
+     * @param  string  $key
+     * @param  int  $amount
+     * @return $this
+     */
+    public function increment(string $key, int $amount = 1)
+    {
+        $this->add(
+            $key,
+            (int) $this->get($key, 0) + $amount,
+        );
+
+        return $this;
+    }
+
+    /**
+     * Decrement a context counter.
+     *
+     * @param  string  $key
+     * @param  int  $amount
+     * @return $this
+     */
+    public function decrement(string $key, int $amount = 1)
+    {
+        return $this->increment($key, $amount * -1);
+    }
+
     /**
      * Determine if the given value is in the given stack.
      *
diff --git a/src/Illuminate/Support/Facades/Context.php b/src/Illuminate/Support/Facades/Context.php
index e1bc10a1bb2e..2c9b197f95ac 100644
--- a/src/Illuminate/Support/Facades/Context.php
+++ b/src/Illuminate/Support/Facades/Context.php
@@ -25,6 +25,8 @@
  * @method static mixed pop(string $key)
  * @method static \Illuminate\Log\Context\Repository pushHidden(string $key, mixed ...$values)
  * @method static mixed popHidden(string $key)
+ * @method static mixed increment(string $key, int $amount)
+ * @method static mixed decrement(string $key, int $amount)
  * @method static bool stackContains(string $key, mixed $value, bool $strict = false)
  * @method static bool hiddenStackContains(string $key, mixed $value, bool $strict = false)
  * @method static mixed scope(callable $callback, array $data = [], array $hidden = [])
diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php
index 657b2fa16fa1..dfb76df9194b 100644
--- a/tests/Log/ContextTest.php
+++ b/tests/Log/ContextTest.php
@@ -602,6 +602,38 @@ public function test_can_rebind_to_separate_class()
 
         file_put_contents($path, '');
     }
+
+    public function test_it_increments_a_counter()
+    {
+        Context::increment('foo');
+        $this->assertSame(1, Context::get('foo'));
+
+        Context::increment('foo');
+        $this->assertSame(2, Context::get('foo'));
+    }
+
+    public function test_it_custom_increments_a_counter()
+    {
+        Context::increment('foo', 2);
+        $this->assertSame(2, Context::get('foo'));
+
+        Context::increment('foo', 3);
+        $this->assertSame(5, Context::get('foo'));
+    }
+
+    public function test_it_decrements_a_counter()
+    {
+        Context::increment('foo');
+        Context::decrement('foo');
+        $this->assertSame(0, Context::get('foo'));
+    }
+
+    public function test_it_custom_decrements_a_counter()
+    {
+        Context::increment('foo', 2);
+        Context::decrement('foo', 2);
+        $this->assertSame(0, Context::get('foo'));
+    }
 }
 
 enum Suit


Reviews:
user: login: AndrewMast
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T14:41:31Z
user: login: mattmcdev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T15:01:49Z

Review comments:
diff_hunk: @@ -369,6 +369,35 @@ public function popHidden($key)
         return array_pop($this->hidden[$key]);
     }
 
+    /**
+     * Increment a context counter.
+     *
+     * @param  string  $key
+     * @param  int  $amount
+     * @return $this
+     */
+    public function increment(string $key, int $amount = 1)
+    {
+        $this->add(
+            $key,
+            (int) $this->get($key, 0) + $amount,
+        );
+
+        return $this;
+    }
+
+    /**
+     * Decrement a context counter.
+     *
+     * @param  string  $key
+     * @param  int  $amount
+     * @return $this
+     */
+    public function decrement(string $key, int $amount = 1)
+    {
+        return $this->increment($key, $amount * -1);
path: src/Illuminate/Log/Context/Repository.php
user: login: AndrewMast
body: Would `-$amount` be the same as `$amount * -1`? And is it any better performance-wise?
created_at: 2025-03-11T14:41:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -369,6 +369,35 @@ public function popHidden($key)
         return array_pop($this->hidden[$key]);
     }
 
+    /**
+     * Increment a context counter.
+     *
+     * @param  string  $key
+     * @param  int  $amount
+     * @return $this
+     */
+    public function increment(string $key, int $amount = 1)
+    {
+        $this->add(
+            $key,
+            (int) $this->get($key, 0) + $amount,
+        );
+
+        return $this;
+    }
+
+    /**
+     * Decrement a context counter.
+     *
+     * @param  string  $key
+     * @param  int  $amount
+     * @return $this
+     */
+    public function decrement(string $key, int $amount = 1)
+    {
+        return $this->increment($key, $amount * -1);
path: src/Illuminate/Log/Context/Repository.php
user: login: mattmcdev
body: Yes, but matching the way it's done elsewhere in the framework. Lots of examples, but e.g. https://github.com/laravel/framework/blob/e6753fc3836bb5fdddd1ee95835d8065bd7c5b16/src/Illuminate/Session/Store.php#L450
created_at: 2025-03-11T15:01:49Z
author_association: CONTRIBUTOR

PR comments:
user: login: henzeb
created_at: 2025-03-11T10:36:53Z
body: i'd opt for increment, like the Cache facade and Eloquent models have?
author_association: CONTRIBUTOR
user: login: mattmcdev
created_at: 2025-03-11T10:49:09Z
body: > i'd opt for increment, like the Cache facade and Eloquent models have?

Agreed
author_association: CONTRIBUTOR
user: login: christophrumpel
created_at: 2025-03-13T11:47:22Z
body: @mattmcdev Hey 👋

Do you have a real-world example of how you would use these new methods? Need a good one for the next Whats New video please :-)
author_association: CONTRIBUTOR
user: login: mattmcdev
created_at: 2025-03-13T11:52:58Z
body: > @mattmcdev Hey 👋
> 
> Do you have a real-world example of how you would use these new methods? Need a good one for the next Whats New video please :-)

Yep - using Laravel Excel with queued chunked imports, can easily keep track of the number of records inserted throughout that import job.
author_association: CONTRIBUTOR
user: login: christophrumpel
created_at: 2025-03-13T11:56:21Z
body: Ah, that's interesting, thanks 🙏
author_association: CONTRIBUTOR