Installation:
composer require outrightvision/api-model
No additional configuration is required—just start using the package.
Create Your First Model:
Define a model class extending OUTRIGHTVision\ApiModel:
// app/Api/Models/User.php
namespace App\Api\Models;
use OUTRIGHTVision\ApiModel;
class User extends ApiModel {}
Instantiate with API Data: Pass raw API response data to the model:
$userData = json_decode(file_get_contents('api-response.json'), true);
$user = new User($userData['data']);
echo $user->name; // Access properties directly
Basic Relationship Casting:
Define $cast_model to auto-cast nested objects:
class User extends ApiModel {
protected $cast_model = [
'company' => Company::class,
'posts' => Post::class,
];
}
Now access relationships like Eloquent:
echo $user->company->name; // "Romaguera-Crona"
Use this package to rapidly wrap a third-party API or expose legacy data as Laravel-like models. Example:
// Fetch data from an external API
$response = Http::get('https://jsonplaceholder.typicode.com/users/1');
$user = new User($response->json()['data']);
// Use relationships
echo $user->company->catchPhrase; // "Multi-layered client-server neural-net"
Standard Models:
Extend ApiModel and define $cast_model for nested objects:
class User extends ApiModel {
protected $cast_model = [
'address' => Address::class,
'company' => Company::class,
];
}
Dynamic Casting:
Use get_data() helper for dot/arrow notation access:
$lat = get_data($user, 'company.geo->lat'); // "-35.3159"
Single Relationships (belongsTo/hasOne):
Define methods to lazy-load relationships:
class User extends ApiModel {
public function company() {
return $this->belongsTo(Company::class, 'company');
}
}
Access via $user->company (lazy-loaded).
HasMany Relationships:
Cast collections and eager-load with included_default:
class User extends ApiModel {
protected $included_default = ['posts'];
public function posts() {
return $this->hasMany(Post::class, 'posts');
}
}
Custom Date Handling:
Override $cast_dates to change default Carbon parsing:
class User extends ApiModel {
protected $cast_dates = ['created_at' => 'Y-m-d H:i:s'];
}
Required Parameters:
Enforce validation via $requiredParameters:
class User extends ApiModel {
protected $requiredParameters = ['id', 'email'];
}
API Resources:
Convert ApiModel to Laravel’s ApiResource for responses:
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource {
public function toArray($request) {
return (new User($this->resource))->toArray();
}
}
Service Providers: Bind models to the container for dependency injection:
$this->app->bind('api.user', function () {
return new User($this->fetchApiData());
});
Modularize Models:
Group models under App\Api\Models and use namespaces for clarity.
Example:
/app
/Api
/Models
User.php
Company.php
Post.php
Lazy-Loading Strategy: Avoid eager-loading relationships unless necessary (default behavior is lazy).
Type Safety: Use PHP 8’s constructor property promotion for stricter typing:
class User extends ApiModel {
public function __construct(
public int $id,
public string $name,
public array $data
) {
parent::__construct($data);
}
}
Testing: Mock API responses in tests:
$mockData = ['data' => ['id' => 1, 'name' => 'Test']];
$user = new User($mockData);
$this->assertEquals('Test', $user->name);
Relationships Not Auto-Discovered:
$cast_model does not automatically create relationship methods.belongsTo/hasMany methods for lazy-loading:
public function company() {
return $this->belongsTo(Company::class, 'company');
}
Date Parsing Overhead:
Carbon::parse() may slow down high-frequency API calls.$cast_dates:
protected $cast_dates = []; // Disable all date casting
Nested Relationships:
user->company->geo) may cause performance issues if not lazy-loaded.get_data() for one-off accesses:
$lat = get_data($user, 'company.geo->lat');
Circular References:
User->Company and Company->Users) can cause infinite loops.$cast_model definitions or use get_data() for safe access.Timezone Mismatches:
date_timezone may not match your application’s timezone.protected $date_timezone = 'America/New_York';
Inspect Raw Data:
Use dd($this->attributes) in a model’s __construct to verify data structure.
Check Casting Logic:
Override castAttribute() to debug casting:
protected function castAttribute($key, $value) {
if ($key === 'company' && !($value instanceof Company)) {
dd("Company not casted: ", $value);
}
return parent::castAttribute($key, $value);
}
Lazy-Loading Debugging:
Add a debug() method to track relationship loading:
public function debug() {
echo "Relationships: " . print_r($this->relationships, true);
}
Custom Casting:
Extend ApiModel to add custom type casting:
class User extends ApiModel {
protected $casts = [
'is_active' => 'boolean',
'score' => 'float',
];
}
Hooks for API Calls:
Override fetch() to intercept API requests:
protected function fetch($url) {
$this->logRequest($url);
return parent::fetch($url);
}
Global Configuration: Set defaults in a service provider:
ApiModel::setDefaultTimezone('UTC');
ApiModel::disableDateCasting();
Custom Helpers:
Extend get_data() for project-specific needs:
function get_data_with_default($data, $path, $default = null) {
return get_data($data, $path) ?? $default;
}
Disable Unused Features:
class User extends ApiModel {
protected $cast_model = []; // Disable all casting
protected $cast_dates = []; // Disable date parsing
}
Cache Relationships: Manually cache loaded relationships:
if (!$this->relationships['company']) {
$this->relationships['company'] = new Company($this->company);
}
Batch Loading:
For hasMany, fetch all data at once:
$posts = collect($this->posts['data'])->map(fn($post) => new Post($post));
How can I help you explore Laravel packages today?