genjisan's avatar

Routing / Controller Architecture

I'm somewhat new to Laravel and have a question for the community regarding API architecture and routing. For some background, I'm refactoring an old symfony project into a Laravel project for bi-directional data sync between Salesforce and an e-commerce store. I'm using the omniphx\forest library as a service provider to run API operations to Salesforce. I'm receiving webhooks from both services and updating each service via a queue to call their respective APIs in order to keep data synced between services.

I have a Salesforce controller whose constructor authenticates forest with Salesforce. I have a bunch of functions in my controller for CRUD operations and SOQL queries. I've been creating API routes and passing a route param to the controller function.

Example:

Route::get('/SalesForce/getSFAccountByEmail/{accountEmail}', [SalesForce::class, 'getSFAccountByEmail']);

This works fine, except now I'm realizing that I'll often be calling these Salesforce functions from another controller for a different API service. In that case I probably don’t want to pass the request and extract a route param within my Salesforce controller function.

public function getSFAccountByEmail(Request $request) {
        $accountEmail = $request->route()->parameter('accountEmail');
		....

I've thought about making the route param optional and the function param optional, but that seems like it might get confusing. I've also though about type hinting an interface but that seems unnecessarily complicated and will lead to repeated code. I could also separate my Salesforce functions into one controller and have another controller to get the route params and call my Salesforce class functions.

What's the best practice for architecting a flexible design? Or is there some other design pattern I should consider that would avoid this issue altogether?

0 likes
1 reply
LaryAI's avatar
Level 58

It sounds like you're looking for a way to organize your controllers and routes in a way that allows for reusability and separation of concerns. Here's a solution that might help you structure your Laravel application more effectively.

Firstly, consider separating your business logic from your HTTP controllers. You can create a service class that handles the interaction with Salesforce. This service can be injected into any controller that requires it, thus avoiding the need to duplicate code.

Here's an example of how you might structure your service and controllers:

  1. Create a SalesforceService class that contains all the logic for interacting with Salesforce.
namespace App\Services;

class SalesforceService
{
    public function __construct()
    {
        // Authenticate with Salesforce here
    }

    public function getSFAccountByEmail($email)
    {
        // Your logic to get the Salesforce account by email
    }

    // Other Salesforce-related methods...
}
  1. Inject this service into your controllers as needed. For example, your SalesforceController might look like this:
namespace App\Http\Controllers;

use App\Services\SalesforceService;
use Illuminate\Http\Request;

class SalesforceController extends Controller
{
    protected $salesforceService;

    public function __construct(SalesforceService $salesforceService)
    {
        $this->salesforceService = $salesforceService;
    }

    public function getSFAccountByEmail($accountEmail)
    {
        $account = $this->salesforceService->getSFAccountByEmail($accountEmail);
        // Return a response with the account data
    }

    // Other methods that use the SalesforceService...
}
  1. Define your routes to use this controller method:
use App\Http\Controllers\SalesforceController;

Route::get('/SalesForce/getSFAccountByEmail/{accountEmail}', [SalesforceController::class, 'getSFAccountByEmail']);
  1. If you need to use the Salesforce service in another controller, you can inject it there as well:
namespace App\Http\Controllers;

use App\Services\SalesforceService;

class AnotherController extends Controller
{
    protected $salesforceService;

    public function __construct(SalesforceService $salesforceService)
    {
        $this->salesforceService = $salesforceService;
    }

    public function someMethod()
    {
        // Use the Salesforce service here
        $account = $this->salesforceService->getSFAccountByEmail('[email protected]');
        // Do something with the account
    }
}

By using a service class, you can avoid duplicating code and keep your controllers clean and focused on handling HTTP requests. This approach also makes it easier to test your application since you can mock the service class in your tests.

Remember to register your service class in a service provider if it requires any dependencies or if you want to take advantage of Laravel's automatic dependency injection.

This architecture follows the Service Pattern and is a common way to organize business logic in Laravel applications. It keeps your controllers slim and your code more maintainable.

Please or to participate in this conversation.