Great question! You're right to feel that something is "off" about putting all your API logic in the controller. In Laravel (and most MVC frameworks), controllers should be kept as thin as possible—their job is to handle requests and responses, not to fetch or process data directly.
Best practice:
Move your API-fetching logic into a dedicated class. In Laravel, this is often done in a "service" class, or sometimes in a model if the data maps closely to a database table. Since your data comes from an external API (not your own database), a service class is most appropriate.
How to Refactor
1. Create a Service Class
Create a new file at app/Services/PropertyApiService.php:
<?php
namespace App\Services;
use GuzzleHttp\Client;
class PropertyApiService
{
protected $client;
protected $apiKey;
protected $baseUrl;
public function __construct()
{
$this->client = new Client();
$this->apiKey = '0123456789';
$this->baseUrl = 'https://api.blahblahblah.com/api/v2/OData/nabor/Property';
}
public function getPropertyByListingId($listingId)
{
$select = '$select=AccessibilityFeatures,Appliances,ArchitecturalStyle,...'; // (shortened for clarity)
$filter = "\$filter=ListingId eq '$listingId'";
$url = "{$this->baseUrl}?access_token={$this->apiKey}&{$filter}&{$select}";
$response = $this->client->get($url);
return json_decode($response->getBody(), true);
}
}
(Fill in the full $select string as needed.)
2. Use the Service in Your Controller
Update your controller to use this service:
namespace App\Http\Controllers;
use App\Services\PropertyApiService;
class PropertyController extends Controller
{
protected $propertyApi;
public function __construct(PropertyApiService $propertyApi)
{
$this->propertyApi = $propertyApi;
}
public function getProperty()
{
try {
$data = $this->propertyApi->getPropertyByListingId('225037268');
return view('property', ['propertyData' => $data]);
} catch (\Exception $e) {
return view('api_error', ['error' => $e->getMessage()]);
}
}
}
3. Bind the Service (Optional)
If you want to use dependency injection (as above), Laravel will auto-resolve the service if you use the default service provider structure.
Summary
- Controllers: Handle HTTP requests, pass data to views.
- Service classes: Handle API calls, business logic, and data processing.
- Models: Represent database tables (not needed here unless you want to store API data locally).
Your controller should be thin and delegate the API logic to a service class.
This makes your code easier to test, maintain, and extend.
Extra:
If you want to keep things even more organized, you can create a DTO (Data Transfer Object) or ViewModel to format the API data before passing it to the view, but that's optional for now.
In summary:
Move the API-fetching logic out of your controller and into a dedicated service class.
Keep your controller focused on handling requests and returning views.
Let me know if you need a more detailed example or have questions about dependency injection!