How to share one method to all controllers in Laravel?
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
How to Share Logic Across Laravel Controllers: A Senior Developer's Guide
As developers working within the Laravel ecosystem, we constantly strive for code reuse, maintainability, and adherence to the DRY (Don't Repeat Yourself) principle. When you find yourself needing to implement the exact same logic—like preparing data, handling routes, or rendering views—across multiple controllers, it signals that a structural improvement is needed.
The challenge you are facing—sharing a single method while managing varying Dependencies Injection (DI), view names, and route parameters—is common. Simply putting this logic directly into every controller violates the principles of good object-oriented design. As senior developers, we need patterns that promote separation of concerns.
This guide will explore several methods for achieving method sharing in Laravel, focusing on the most robust, scalable, and idiomatic solutions.
The Pitfalls of Direct Duplication
Before diving into solutions, consider why simply copying code into multiple controllers is problematic. If you change how data is fetched or how a route is generated, you must update every single controller instance. This creates brittle code that is difficult to maintain and debug. We want our architecture to be flexible, which means decoupling the what (the business logic) from the how (the HTTP request handling).
Solution 1: Using Traits for Simple Method Sharing
For scenarios where the shared method is relatively simple and doesn't involve complex dependency injection, PHP Traits offer a quick way to share methods across classes.
// app/Traits/RoutePreparer.php
trait RoutePreparer
{
/**
* Prepares common data structures for view rendering.
*/
public function prepareViewData(object $model, string $viewName)
{
$baseData = [
'model' => $model,
// Assuming $model has a relationship or route definition accessible
'route' => route('users.show', $model->id),
];
return view($viewName, $baseData);
}
}
// In a Controller:
class PostController extends Controller
{
use RoutePreparer;
public function show(Post $post)
{
// Now we can reuse the shared logic cleanly
$this->prepareViewData($post, 'posts.show');
}
}
While Traits are excellent for simple method inclusion, they struggle when the required dependencies (like different $model types or dynamic route parameters) become highly varied across controllers.
Solution 2: The Recommended Approach – Service Classes (Action Classes)
For complex requirements involving dynamic DI, routing, and view preparation, the superior architectural pattern in Laravel is to move this shared logic out of the controller and into dedicated Service Classes or Action Classes. This aligns perfectly with Laravel's philosophy of using Eloquent models for data and services for business operations.
Instead of putting the method in the controller, we create a central class that handles the heavy lifting. The controller then becomes a lean orchestrator.
Implementing a Shared Action Class
We can encapsulate your required logic into a dedicated service class. This service will handle fetching data and preparing the necessary context before the view is rendered.
// app/Services/ViewDataPreparer.php
namespace App\Services;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
class ViewDataPreparer
{
/**
* Prepares data and returns a view instance based on dynamic input.
*/
public function renderDynamicView(object $model, string $viewName, array $params = [])
{
$baseData = [
'model' => $model,
// Example of dynamically building a route based on parameters
'route' => Route::route('users.show', [$model->id]),
'custom_params' => $params,
];
return view($viewName, $baseData);
}
}
Controller Integration
Now, your controller becomes very clean and focuses only on handling the request flow:
// app/Http/Controllers/PostController.php
namespace App\Http\Controllers;
use App\Models\Post;
use App\Services\ViewDataPreparer;
use Illuminate\Http\Request;
class PostController extends Controller
{
protected $preparer;
// Use constructor injection for DI (best practice!)
public function __construct(ViewDataPreparer $preparer)
{
$this->preparer = $preparer;
}
public function show(Post $post)
{
// The controller delegates the complex logic to the service
return $this->preparer->renderDynamicView(
$post,
'posts.show',
['context' => 'admin'] // Passing dynamic parameters easily
);
}
}
Conclusion
For sharing methods across Laravel controllers, the choice depends entirely on complexity. For simple method inclusion, Traits are sufficient. However, for complex logic involving dependency injection, dynamic routing, and varied parameter handling—the exact scenario you described—the Service Class pattern is vastly superior. It keeps your controllers thin, promotes strong separation of concerns, and ensures that your application remains scalable and maintainable, following the best practices promoted by the Laravel framework. Always aim to use services when logic crosses controller boundaries!