Usage Patterns

Recommended patterns and best practices

Usage Patterns

This guide covers three recommended patterns for using the OI Laravel INSEE package in your application.

Pattern 1: Facade (Simple Scripts)

Use the Facade for quick, one-off operations in simple contexts:

php
use OiLab\OiLaravelInsee\Facades\INSEE;

$company = INSEE::findSiren('732829320');
echo $company['uniteLegale']['denomination'];

When to Use

  • Simple controllers or routes
  • Quick lookups without complex logic
  • Testing or prototyping
  • Scripts or commands

Example: Simple Route

php
Route::get('/company/{siren}', function (string $siren) {
    $response = INSEE::findSiren($siren);
    
    if ($response['header']['statut'] !== 200) {
        return response('Not found', 404);
    }

    return $response['uniteLegale'];
})->name('company.show');

Example: Artisan Command

php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use OiLab\OiLaravelInsee\Facades\INSEE;

class LookupCompanyCommand extends Command
{
    protected $signature = 'company:lookup {siren}';
    protected $description = 'Look up a company by SIREN';

    public function handle(): int
    {
        $siren = $this->argument('siren');
        $response = INSEE::findSiren($siren);

        if ($response['header']['statut'] !== 200) {
            $this->error('Company not found');
            return 1;
        }

        $company = $response['uniteLegale'];
        
        $this->table(
            ['Field', 'Value'],
            [
                ['SIREN', $company['siren']],
                ['Name', $company['denomination']],
                ['Status', $company['etatAdministratifUniteLegale']],
            ]
        );

        return 0;
    }
}

Inject the Client class for testable, type-hinted code:

php
<?php

namespace App\Http\Controllers;

use OiLab\OiLaravelInsee\Client;

class CompanyController extends Controller
{
    public function __construct(private Client $insee) {}

    public function show(string $siren)
    {
        $response = $this->insee->findSiren($siren);
        
        if ($response['header']['statut'] !== 200) {
            return redirect()->back()->withError('Company not found');
        }

        return view('company.show', ['company' => $response['uniteLegale']]);
    }
}

When to Use

  • Controllers (most common)
  • Services and classes
  • Code that needs testing
  • Production applications
  • Complex business logic

Example: Service Class

php
<?php

namespace App\Services;

use OiLab\OiLaravelInsee\Client;
use App\Models\Company;

class CompanyImportService
{
    public function __construct(private Client $insee) {}

    public function importCompany(string $siren): ?Company
    {
        $response = $this->insee->findSiren($siren);

        if ($response['header']['statut'] !== 200) {
            throw new CompanyNotFoundException("SIREN: $siren");
        }

        $data = $response['uniteLegale'];

        return Company::updateOrCreate(
            ['siren' => $data['siren']],
            [
                'name' => $data['denomination'],
                'status' => $data['etatAdministratifUniteLegale'],
                'activity' => $data['activitePrincipaleUniteLegale'],
                'employees' => $data['trancheEffectifsUniteLegale'],
                'director_name' => $this->extractDirector($response),
            ]
        );
    }

    private function extractDirector(array $response): ?string
    {
        if (!isset($response['dirigeant'])) {
            return null;
        }

        $d = $response['dirigeant'];
        return trim(($d['prenom'] ?? '') . ' ' . ($d['nom'] ?? ''));
    }
}

Example: Resource Controller

php
<?php

namespace App\Http\Controllers;

use OiLab\OiLaravelInsee\Client;
use Illuminate\View\View;

class CompanyController extends Controller
{
    public function __construct(private Client $insee) {}

    public function show(string $siren): View
    {
        $response = $this->insee->findSiren($siren);

        if ($response['header']['statut'] !== 200) {
            abort(404, 'Company not found');
        }

        return view('company.show', ['company' => $response['uniteLegale']]);
    }

    public function search(Request $request): View
    {
        $query = $request->input('q');
        
        if (!$query) {
            return view('company.search', ['results' => []]);
        }

        $response = $this->insee->searchCompanies([
            'q' => $query,
            'nombre' => 20,
        ]);

        $companies = [];
        if ($response['header']['statut'] === 200) {
            $companies = $response['unitesLegales'] ?? [];
        }

        return view('company.search', ['results' => $companies]);
    }
}

Pattern 3: App Helper

Use the app() helper for one-off access without dependency injection:

php
$company = app(Client::class)->findSiren('732829320');

When to Use

  • Closures or callbacks
  • Helper functions
  • When you can't use dependency injection

Example: Route Model Binding

php
Route::bind('company', function (string $siren) {
    $response = app(\OiLab\OiLaravelInsee\Client::class)->findSiren($siren);
    
    if ($response['header']['statut'] !== 200) {
        abort(404);
    }

    return $response['uniteLegale'];
});

Route::get('/company/{company}', function ($company) {
    return view('company.show', ['company' => $company]);
});

Batch Processing Pattern

Look up multiple companies efficiently with error recovery:

php
<?php

namespace App\Services;

use OiLab\OiLaravelInsee\Client;

class BatchCompanyImporter
{
    public function __construct(private Client $insee) {}

    public function importSirens(array $sirens): array
    {
        $results = [
            'success' => [],
            'failed' => [],
        ];

        foreach ($sirens as $siren) {
            try {
                $response = $this->insee->findSiren($siren);

                if ($response['header']['statut'] === 200) {
                    $results['success'][] = $response['uniteLegale'];
                } else {
                    $results['failed'][$siren] = 'Not found';
                }
            } catch (\Exception $e) {
                $results['failed'][$siren] = $e->getMessage();
            }

            // Be respectful of INSEE rate limits
            usleep(100000); // 100ms delay between requests
        }

        return $results;
    }
}

Usage in a Command

php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\BatchCompanyImporter;

class ImportCompaniesCommand extends Command
{
    public function __construct(private BatchCompanyImporter $importer) {}

    public function handle(): int
    {
        $sirens = $this->argument('sirens');
        
        $this->info('Importing ' . count($sirens) . ' companies...');

        $results = $this->importer->importSirens($sirens);

        $this->info('Successfully imported: ' . count($results['success']));
        $this->error('Failed: ' . count($results['failed']));

        foreach ($results['failed'] as $siren => $reason) {
            $this->line("  - $siren: $reason");
        }

        return count($results['failed']) > 0 ? 1 : 0;
    }
}

Caching Pattern

Cache successful results to minimize API calls:

php
<?php

namespace App\Services;

use OiLab\OiLaravelInsee\Client;
use Illuminate\Support\Facades\Cache;

class CachedInseeService
{
    public function __construct(private Client $insee) {}

    public function findCompany(string $siren, int $cacheTtl = 86400): array
    {
        $cacheKey = "insee:company:$siren";

        return Cache::remember($cacheKey, $cacheTtl, function () use ($siren) {
            return $this->insee->findSiren($siren);
        });
    }

    public function searchCompanies(
        string $query,
        int $limit = 20,
        int $cacheTtl = 3600
    ): array {
        $cacheKey = "insee:search:" . md5($query . $limit);

        return Cache::remember($cacheKey, $cacheTtl, function () use ($query, $limit) {
            return $this->insee->searchCompanies([
                'q' => $query,
                'nombre' => $limit,
            ]);
        });
    }

    public function invalidateCompanyCache(string $siren): void
    {
        Cache::forget("insee:company:$siren");
    }
}

Testing Pattern

Test your code that uses INSEE without hitting the real API:

php
<?php

use OiLab\OiLaravelInsee\Client;

it('displays company information', function () {
    $mockResponse = [
        'header' => ['statut' => 200, 'message' => 'OK'],
        'uniteLegale' => [
            'siren' => '732829320',
            'denomination' => 'Apple France SARL',
            'etatAdministratifUniteLegale' => 'A',
        ],
    ];

    // Mock the INSEE client
    $this->mock(Client::class, function ($mock) use ($mockResponse) {
        $mock->shouldReceive('findSiren')
            ->with('732829320')
            ->andReturn($mockResponse);
    });

    // Your test
    $response = $this->get('/company/732829320');

    $response->assertSee('Apple France SARL');
});

Error Recovery Pattern

Implement robust error handling with fallbacks:

php
<?php

namespace App\Services;

use OiLab\OiLaravelInsee\Client;

class RobustInseeService
{
    public function __construct(private Client $insee) {}

    public function findCompanyWithFallback(string $siren): array
    {
        // Try live API
        $response = $this->insee->findSiren($siren);

        if ($response['header']['statut'] === 200) {
            return $response;
        }

        // Try cache
        $cached = cache("company:$siren:full");
        if ($cached) {
            return [
                ...$cached,
                'from_cache' => true,
                'header' => [
                    ...$cached['header'],
                    'message' => 'Served from cache',
                ],
            ];
        }

        // Return error
        return $response;
    }

    public function findCompanyWithRetry(string $siren, int $maxRetries = 3): array
    {
        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            $response = $this->insee->findSiren($siren);

            if ($response['header']['statut'] === 200) {
                return $response;
            }

            if ($attempt < $maxRetries) {
                sleep(1 * $attempt); // Exponential backoff
            }
        }

        return $response;
    }
}

Queue Pattern

Process lookups asynchronously:

php
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use OiLab\OiLaravelInsee\Client;
use App\Models\Company;

class ImportCompanyDataJob implements ShouldQueue
{
    use Queueable;

    public function __construct(public string $siren) {}

    public function handle(Client $insee): void
    {
        $response = $insee->findSiren($this->siren);

        if ($response['header']['statut'] !== 200) {
            $this->fail(new \Exception('Company not found'));
            return;
        }

        $data = $response['uniteLegale'];

        Company::updateOrCreate(
            ['siren' => $data['siren']],
            [
                'name' => $data['denomination'],
                'status' => $data['etatAdministratifUniteLegale'],
                'activity' => $data['activitePrincipaleUniteLegale'],
            ]
        );
    }
}

Rate Limit Awareness

Be mindful of INSEE rate limits:

php
// Add delays between requests
foreach ($sirens as $siren) {
    $response = $insee->findSiren($siren);
    usleep(250000); // 250ms between requests
}

// Or cache aggressively
cache()->rememberForever("company:$siren", function () use ($insee, $siren) {
    return $insee->findSiren($siren);
});

// Or batch requests
$results = $insee->searchCompanies([
    'q' => 'search terms',
    'nombre' => 100, // Get more results per request
]);

Tips

  • Prefer dependency injection for production code
  • Use the Facade for quick scripts and commands
  • Cache results to minimize API calls
  • Implement retry logic for transient failures
  • Be respectful of INSEE rate limits
  • Always handle errors gracefully
  • Test your code with mocks
  • Log all API interactions for debugging
Project under MIT License.
Design by