DeveloperBreeze

In many Laravel applications, some data is frequently accessed across multiple views, controllers, or middleware. Examples include global configurations, user preferences, or resource limits. Instead of querying the database repeatedly, you can use the singleton pattern to load such data once and make it reusable throughout your application. This tutorial explains how to implement the singleton pattern in Laravel for efficient data sharing.


Scenario: Shared Data via Singleton

For this tutorial, imagine an application where:

  • Application Limits: Data like maximum uploads or API rate limits are shared globally.
  • Feature Toggles: Enable or disable application features dynamically.
  • Global Preferences: Shared preferences such as the preferred user interface theme.

Using the singleton pattern, we’ll make this data globally accessible and reusable.


Step 1: Create a Service Provider for the Singleton

Laravel’s service container provides a simple way to implement the singleton pattern.

  1. Generate a Service Provider:
   php artisan make:provider SharedDataServiceProvider
  1. Register the Singleton:

Open the generated file in app/Providers/SharedDataServiceProvider.php and define a singleton for the shared data:

   namespace App\Providers;

   use Illuminate\Support\ServiceProvider;

   class SharedDataServiceProvider extends ServiceProvider
   {
       public function register()
       {
           $this->app->singleton('sharedData', function () {
               return (object) [
                   'maxUploads' => 20, // Maximum file uploads allowed
                   'apiRateLimit' => 100, // API requests per minute
                   'theme' => 'dark', // Default UI theme
               ];
           });
       }
   }
  1. Register the Provider:

Add the provider to the providers array in config/app.php:

   'providers' => [
       // Other service providers
       App\Providers\SharedDataServiceProvider::class,
   ],

Step 2: Access the Singleton in Controllers

You can access the shared data singleton using the app() helper or dependency injection.

  1. Using the app() Helper:

In any controller, retrieve the shared data:

   namespace App\Http\Controllers;

   class HomeController extends Controller
   {
       public function index()
       {
           $sharedData = app('sharedData');

           return view('home', [
               'maxUploads' => $sharedData->maxUploads,
               'apiRateLimit' => $sharedData->apiRateLimit,
               'theme' => $sharedData->theme,
           ]);
       }
   }
  1. Dependency Injection:

Alternatively, inject the singleton into the controller’s constructor:

   namespace App\Http\Controllers;

   class HomeController extends Controller
   {
       protected $sharedData;

       public function __construct()
       {
           $this->sharedData = app('sharedData');
       }

       public function index()
       {
           return view('home', [
               'maxUploads' => $this->sharedData->maxUploads,
               'apiRateLimit' => $this->sharedData->apiRateLimit,
               'theme' => $this->sharedData->theme,
           ]);
       }
   }

Step 3: Access Singleton Data in Views

You can also share the singleton data globally in views using the View::share method.

  1. Update the boot Method in the Service Provider:

Add the following to share the singleton globally in Blade templates:

   public function boot()
   {
       view()->share('sharedData', app('sharedData'));
   }
  1. Access in Blade Templates:

Now, the shared data is available in all views:

   <h1>Theme: {{ $sharedData->theme }}</h1>
   <p>API Rate Limit: {{ $sharedData->apiRateLimit }}</p>
   <p>Max Uploads: {{ $sharedData->maxUploads }}</p>

Step 4: Refresh Singleton Data Dynamically

If the data in the singleton changes frequently, you can refresh it when needed.

  1. Clear the Singleton Instance:

Use the following code to reset the singleton:

   app()->forgetInstance('sharedData');
  1. Re-register the Singleton:

After resetting, access app('sharedData') to reload the data dynamically.

  1. Trigger Refresh on Data Updates:

Listen to relevant events and refresh the singleton when necessary:

   Event::listen('settings.updated', function () {
       app()->forgetInstance('sharedData');
   });

Step 5: Benefits of the Singleton Pattern

  • Performance Improvement: Avoid redundant database queries by loading shared data once.
  • Centralized Data Logic: Keep shared data in a single location for easy maintenance.
  • Reusability: Access the same data instance anywhere in the application without reloading.

Conclusion

By using the singleton pattern in Laravel, you’ve optimized how shared data is managed and accessed across your application. This approach ensures efficient performance and consistent data usage, making it ideal for managing application-wide settings, preferences, or resource limits.


Continue Reading

Discover more amazing content handpicked just for you

Tutorial
php

Handling Race Conditions in Laravel Jobs and Queues

Laravel provides an atomic lock mechanism using Redis to prevent concurrent access.

   use Illuminate\Support\Facades\Cache;

   public function handle()
   {
       $lock = Cache::lock('order_' . $this->order->id, 10); // 10 seconds TTL

       if ($lock->get()) {
           try {
               // Process the order safely
               $this->order->process();
           } finally {
               $lock->release();
           }
       } else {
           Log::warning('Order already being processed: ' . $this->order->id);
       }
   }

Nov 16, 2024
Read More
Tutorial
php

Resolving N+1 Query Problems in Laravel

The N+1 query problem happens when your application executes one query to retrieve a parent dataset, followed by multiple additional queries to fetch related data for each parent record.

Imagine you want to fetch a list of posts along with their authors:

Nov 16, 2024
Read More
Tutorial
php

Laravel Best Practices for Sharing Data Between Views and Controllers

   namespace App\Providers;

   use Illuminate\Support\ServiceProvider;
   use Illuminate\Support\Facades\View;

   class GlobalDataServiceProvider extends ServiceProvider
   {
       public function boot()
       {
           $globalConfig = [
               'app_mode' => 'live', // Example: Maintenance or live mode
               'support_email' => 'support@example.com',
           ];

           View::share('globalConfig', $globalConfig);
       }
   }
   <p>App Mode: {{ $globalConfig['app_mode'] }}</p>
   <p>Contact: {{ $globalConfig['support_email'] }}</p>

Nov 16, 2024
Read More
Tutorial
php

Optimizing Performance in Laravel by Centralizing Data Loading

   namespace App\Http\Controllers;

   class ExampleController extends Controller
   {
       protected $sharedData;

       public function __construct()
       {
           $this->sharedData = app('sharedData');
       }

       public function index()
       {
           return view('example', [
               'maxUploads' => $this->sharedData['max_uploads'],
               'apiRateLimit' => $this->sharedData['api_rate_limit'],
           ]);
       }
   }

To share the centralized data globally in Blade templates:

Nov 16, 2024
Read More
Tutorial
php

Building a Base Controller for Reusable Data Access in Laravel

The Base Controller can also include methods that multiple controllers can use.

Add reusable methods to handle common tasks:

Nov 16, 2024
Read More
Tutorial
php

Leveraging Service Providers to Manage Global Data in Laravel

Edit the boot method in app/Providers/CustomDataServiceProvider.php:

   namespace App\Providers;

   use Illuminate\Support\ServiceProvider;

   class CustomDataServiceProvider extends ServiceProvider
   {
       public function boot()
       {
           // Example data
           $globalPreferences = [
               'api_limit' => 100,
               'app_mode' => 'live', // 'maintenance' or 'live'
               'feedback_form_enabled' => true,
           ];

           // Share this data globally
           view()->share('globalPreferences', $globalPreferences);
       }
   }

Nov 16, 2024
Read More
Tutorial
javascript

Advanced JavaScript Patterns: Writing Cleaner, Faster, and More Maintainable Code

function Subject() {
    this.observers = [];
}

Subject.prototype = {
    subscribe: function(fn) {
        this.observers.push(fn);
    },
    unsubscribe: function(fnToRemove) {
        this.observers = this.observers.filter(fn => fn !== fnToRemove);
    },
    notify: function(data) {
        this.observers.forEach(fn => fn(data));
    }
};

const subject = new Subject();

function Observer1(data) {
    console.log('Observer 1:', data);
}

function Observer2(data) {
    console.log('Observer 2:', data);
}

subject.subscribe(Observer1);
subject.subscribe(Observer2);

subject.notify('Hello Observers!');
// Output:
// Observer 1: Hello Observers!
// Observer 2: Hello Observers!

subject.unsubscribe(Observer2);

subject.notify('Hello Observer 1!');
// Output:
// Observer 1: Hello Observer 1!
  • Decouples the subject from its observers.
  • Supports dynamic subscription and notification.

Aug 27, 2024
Read More

Discussion 0

Please sign in to join the discussion.

No comments yet. Start the discussion!