Backend Development
Learn how to build robust backend functionality for your Auto Commerce modules.
Service Layer
The service layer contains your module's core business logic.
Creating a Service
namespace Modules\YourModule\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class YourModuleService
{
protected $apiUrl;
protected $apiKey;
protected $tenant;
public function __construct()
{
$this->tenant = tenancy()->tenant;
$this->loadSettings();
}
protected function loadSettings()
{
$this->apiUrl = ModuleManager::getSetting('your-module', 'api_url', $this->tenant);
$this->apiKey = ModuleManager::getSetting('your-module', 'api_key', $this->tenant);
}
public function syncData()
{
try {
$data = $this->fetchFromApi();
$this->processData($data);
Log::info('Data synced successfully', [
'module' => 'your-module',
'tenant' => $this->tenant->id
]);
return ['success' => true, 'count' => count($data)];
} catch (\Exception $e) {
Log::error('Sync failed', [
'module' => 'your-module',
'error' => $e->getMessage()
]);
throw $e;
}
}
protected function fetchFromApi()
{
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $this->apiKey,
'Accept' => 'application/json'
])
->timeout(30)
->get($this->apiUrl . '/endpoint');
if (!$response->successful()) {
throw new \Exception('API request failed: ' . $response->body());
}
return $response->json();
}
protected function processData($data)
{
foreach ($data as $item) {
// Process each item
$this->processItem($item);
}
}
protected function processItem($item)
{
// Your processing logic
}
}
Dependency Injection
Use Laravel's service container for dependencies:
namespace Modules\YourModule\Services;
use App\Core\Services\OrderService;
use App\Core\Services\CustomerService;
class YourModuleService
{
protected $orderService;
protected $customerService;
public function __construct(
OrderService $orderService,
CustomerService $customerService
) {
$this->orderService = $orderService;
$this->customerService = $customerService;
}
public function createOrderFromExternal($externalData)
{
// Find or create customer
$customer = $this->customerService->findOrCreateFromExternal($externalData['customer']);
// Create order
$order = $this->orderService->create([
'customer_id' => $customer->id,
'source_module' => 'your-module',
'external_id' => $externalData['id'],
'items' => $externalData['items'],
'total' => $externalData['total']
]);
return $order;
}
}
Controllers
API Controllers
namespace Modules\YourModule\Http\Controllers\Api;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use Modules\YourModule\Services\YourModuleService;
use Modules\YourModule\Http\Requests\ConnectRequest;
class YourModuleController extends Controller
{
protected $service;
public function __construct(YourModuleService $service)
{
$this->service = $service;
}
/**
* Connect to external service
*/
public function connect(ConnectRequest $request): JsonResponse
{
$validated = $request->validated();
// Save settings
ModuleManager::setSetting('your-module', tenancy()->tenant, $validated);
// Test connection
$status = $this->service->testConnection();
if (!$status['success']) {
return response()->json([
'message' => 'Connection failed: ' . $status['error']
], 400);
}
return response()->json([
'message' => 'Connected successfully',
'data' => $status
]);
}
/**
* Sync data from external service
*/
public function sync(Request $request): JsonResponse
{
$request->validate([
'since' => 'nullable|date'
]);
$result = $this->service->syncData($request->input('since'));
return response()->json([
'message' => 'Sync completed',
'data' => $result
]);
}
/**
* Get connection status
*/
public function status(): JsonResponse
{
$isConnected = ModuleManager::isEnabled('your-module', tenancy()->tenant);
$settings = ModuleManager::getSettings('your-module', tenancy()->tenant);
return response()->json([
'connected' => $isConnected,
'settings' => $settings,
'last_sync' => Cache::get('your-module.last_sync')
]);
}
}
Form Requests
Validate incoming data:
namespace Modules\YourModule\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ConnectRequest extends FormRequest
{
public function authorize()
{
return $this->user()->can('manage-modules');
}
public function rules()
{
return [
'api_url' => 'required|url',
'api_key' => 'required|string|min:20',
'api_secret' => 'required|string|min:20'
];
}
public function messages()
{
return [
'api_key.min' => 'API key appears to be invalid. Please check your credentials.',
'api_url.url' => 'Please provide a valid API URL.'
];
}
}
Events & Listeners
Dispatching Events
namespace Modules\YourModule\Services;
use Modules\YourModule\Events\DataSynced;
class YourModuleService
{
public function syncData()
{
$data = $this->fetchFromApi();
$processed = $this->processData($data);
// Dispatch event
event(new DataSynced($processed));
return $processed;
}
}
Creating Events
namespace Modules\YourModule\Events;
use Illuminate\Queue\SerializesModels;
class DataSynced
{
use SerializesModels;
public $data;
public $timestamp;
public function __construct($data)
{
$this->data = $data;
$this->timestamp = now();
}
}
Creating Listeners
namespace Modules\YourModule\Listeners;
use Modules\YourModule\Events\DataSynced;
use Illuminate\Support\Facades\Log;
class LogDataSynced
{
public function handle(DataSynced $event)
{
Log::info('Data synced from YourModule', [
'count' => count($event->data),
'timestamp' => $event->timestamp
]);
}
}
Listening to Core Events
// In your ServiceProvider
use App\Core\Events\OrderCreated;
use Modules\YourModule\Listeners\HandleOrderCreated;
public function boot()
{
Event::listen(OrderCreated::class, HandleOrderCreated::class);
}
// Listener
namespace Modules\YourModule\Listeners;
use App\Core\Events\OrderCreated;
class HandleOrderCreated
{
public function handle(OrderCreated $event)
{
$order = $event->order;
// Send order to your external service
app(YourModuleService::class)->sendOrder($order);
}
}
Queue Jobs
Creating Jobs
namespace Modules\YourModule\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Modules\YourModule\Services\YourModuleService;
class ProcessBulkSync implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 300; // 5 minutes
public $tries = 3;
protected $tenantId;
protected $startDate;
protected $endDate;
public function __construct($tenantId, $startDate, $endDate)
{
$this->tenantId = $tenantId;
$this->startDate = $startDate;
$this->endDate = $endDate;
}
public function handle(YourModuleService $service)
{
// Initialize tenant context
tenancy()->initialize($this->tenantId);
// Process sync
$service->syncDataRange($this->startDate, $this->endDate);
// Cleanup
tenancy()->end();
}
public function failed(\Throwable $exception)
{
// Handle failure
Log::error('Bulk sync failed', [
'tenant' => $this->tenantId,
'error' => $exception->getMessage()
]);
// Notify admin
// NotificationService::notifyAdmin(...);
}
}
Dispatching Jobs
// Dispatch immediately
ProcessBulkSync::dispatch($tenantId, $startDate, $endDate);
// Dispatch with delay
ProcessBulkSync::dispatch($tenantId, $startDate, $endDate)
->delay(now()->addMinutes(5));
// Dispatch to specific queue
ProcessBulkSync::dispatch($tenantId, $startDate, $endDate)
->onQueue('sync');
// Chain jobs
ProcessBulkSync::withChain([
new NotifyAdminOfCompletion($tenantId),
new CleanupTempData($tenantId)
])->dispatch($tenantId, $startDate, $endDate);
Database Operations
Query Builder
use Illuminate\Support\Facades\DB;
public function getRecentOrders($limit = 10)
{
return DB::table('orders')
->where('source_module', 'your-module')
->where('created_at', '>=', now()->subDays(7))
->orderBy('created_at', 'desc')
->limit($limit)
->get();
}
Eloquent ORM
use Modules\YourModule\Entities\YourModel;
// Create
$model = YourModel::create([
'tenant_id' => tenancy()->tenant->id,
'field' => 'value'
]);
// Update
$model->update(['field' => 'new value']);
// Delete
$model->delete();
// Query
$models = YourModel::where('tenant_id', tenancy()->tenant->id)
->where('is_active', true)
->with('relation')
->get();
Transactions
use Illuminate\Support\Facades\DB;
public function complexOperation()
{
DB::transaction(function () {
// All or nothing
$order = Order::create([...]);
$shipment = Shipment::create([...]);
$this->externalService->notify($order);
});
}
Error Handling
Try-Catch
public function riskyOperation()
{
try {
$result = $this->externalApiCall();
return $result;
} catch (\GuzzleHttp\Exception\RequestException $e) {
Log::error('API request failed', [
'error' => $e->getMessage(),
'response' => $e->getResponse()?->getBody()?->getContents()
]);
throw new \Exception('Failed to communicate with external service');
} catch (\Exception $e) {
Log::error('Unexpected error', ['error' => $e->getMessage()]);
throw $e;
}
}
Custom Exceptions
namespace Modules\YourModule\Exceptions;
class ApiConnectionException extends \Exception
{
public static function invalidCredentials()
{
return new static('Invalid API credentials provided');
}
public static function rateLimitExceeded()
{
return new static('API rate limit exceeded. Please try again later.');
}
}
// Usage
if (!$this->isValidCredential()) {
throw ApiConnectionException::invalidCredentials();
}
Testing
Unit Tests
namespace Modules\YourModule\Tests\Unit;
use Tests\TestCase;
use Modules\YourModule\Services\YourModuleService;
class YourModuleServiceTest extends TestCase
{
public function test_can_fetch_data()
{
Http::fake([
'*' => Http::response(['data' => 'test'], 200)
]);
$service = app(YourModuleService::class);
$result = $service->fetchData();
$this->assertNotEmpty($result);
}
}
Feature Tests
namespace Modules\YourModule\Tests\Feature;
use Tests\TestCase;
class YourModuleApiTest extends TestCase
{
public function test_can_connect_to_service()
{
$response = $this->postJson('/api/integrations/yourmodule/connect', [
'api_url' => 'https://api.example.com',
'api_key' => 'test-key'
]);
$response->assertStatus(200)
->assertJson(['message' => 'Connected successfully']);
}
}
Next Steps
- Frontend Development - Build UI components
- Module Structure - Understand file organization
- Creating Modules - Quick start guide