1. Go to this page and download the library: Download sanchescom/laravel-rest library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
sanchescom / laravel-rest example snippets
class Post extends Model
{
protected ?string $endpoint = 'posts';
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}
Post::where('status', 'published')->orderBy('created_at')->with('author')->paginate(15);
use Sanchescom\Rest\Model;
class Post extends Model
{
// endpoint is inferred from the class name: "posts"
protected array $casts = ['id' => 'int', 'userId' => 'int'];
}
foreach (Post::where('status', 'active')->lazy() as $post) {
// ...
}
// Mirror an API resource into a local table
Post::lazy(50)->chunk(500)->each(
fn ($posts) => DbPost::upsert($posts->map->toArray()->values()->all(), ['id']),
);
$paginator = User::get()->paginate(15); // slices an already-fetched collection
Product::orderBy('price', 'desc')->orderBy('name')->get();
// GET products?sort=price:desc,name:asc
// (or price|desc,name|asc with the custom separator)
Invoice::post(['number' => 'INV-001', 'total' => 500]);
// POST invoices
// Body: {"data":{"number":"INV-001","total":500}}
Invoice::get(1);
// GET invoices/1
// Response: {"data":{"id":1,"number":"INV-001","total":500}}
// → Invoice with attributes {id:1, number:"INV-001", total:500}
use Sanchescom\Rest\ClientResolver;
use Sanchescom\Rest\Clients\GuzzleClient;
use Sanchescom\Rest\Model;
$resolver = new ClientResolver;
$resolver->addClient('catalog', GuzzleClient::fromConfig([
'base_uri' => 'https://api.example.com/',
]));
$resolver->setDefaultClient('catalog');
$resolver->setQueryConfig('catalog', 'jsonapi'); // preset string
Model::setClientResolver($resolver);
// Or with a full config array:
$resolver->setQueryConfig('catalog', [
'preset' => 'jsonapi',
'sort' => 'suffix',
]);
// List — filters, sort, pagination, casing all applied automatically
Order::where('status', 'pending')
->where('totalAmount', 'gte', 50)
->orderBy('createdAt', 'desc')
->page(2)
->limit(20)
->get();
// GET orders?filter[status]=pending&filter[total_amount][gte]=50
// &sort=created_at:desc&page[size]=20&page[number]=2
// Headers: Authorization: Bearer <token>, X-Store-ID: eu-west-1
// Per-chain header override for a specific locale
Order::withHeaders(['Accept-Language' => 'de-DE'])
->where('status', 'pending')
->get();
// GET orders?filter[status]=pending
// Headers: Authorization: Bearer <token>, X-Store-ID: eu-west-1, Accept-Language: de-DE
// Create — body wrapped in 'data'
Order::post(['customerEmail' => '[email protected]', 'totalAmount' => 99.90]);
// POST orders
// Body: {"data":{"customerEmail":"[email protected]","totalAmount":99.9}}
// Validation error — errors extracted from nested key
try {
Order::post(['customerEmail' => 'bad']);
} catch (\Sanchescom\Rest\Exceptions\ValidationException $e) {
// Response: {"errors":{"fields":{"customer_email":["invalid email"]}}}
$e->errors(); // ['customer_email' => ['invalid email']]
}
// config/rest.php
'cache' => [
'store' => null, // null = default Laravel store; 'redis', 'memcached', etc.
'ttl' => 300, // default TTL in seconds used by withCache() without args
],
class Post extends Model
{
protected ?int $cacheTtl = 60; // seconds; null (default) = caching disabled
}
// Enable caching for one chain (uses model $cacheTtl, or default TTL if not set)
Post::withCache()->get();
// Use a specific TTL just for this chain
Post::withCache(30)->get();
// Force a fresh hit even if the model has $cacheTtl
Post::withoutCache()->get();
Post::post(['title' => 'New']); // POST + flushCache() on success
Post::put(1, ['title' => 'Hi']); // PUT + flushCache() on success
Post::delete(1); // DELETE + flushCache() on success
Post::flushCache(); // safe no-op when no store is configured
Model::setCacheStore(null);
CachedPost::get(); // throws RestException: "no cache store configured"
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;
use Sanchescom\Rest\Model;
Model::setCacheStore(
new Psr16Cache(new FilesystemAdapter),
defaultTtl: 120,
);
// Now any model with $cacheTtl or withCache() will use the file cache
Post::withCache(60)->get(1);
// config/rest.php
'memoize' => (bool) env('REST_MEMOIZE', false),
// or at runtime
Rest::memoize();
$comments = Comment::where('postId', 1)->get();
foreach ($comments as $comment) {
echo $comment->author->name; // one request per distinct author, not per comment
}
use Sanchescom\Rest\Rest;
use Sanchescom\Rest\Model;
// ArrayCache is a test double from this package's tests/ — in your app wire any PSR-16 store instead
Model::setCacheStore(new \Sanchescom\Rest\Tests\Support\ArrayCache);
Rest::fake(['posts' => Rest::response([['id' => 1]])]);
Post::withCache()->page(1)->get();
Post::withCache()->page(2)->get();
Post::withCache()->page(1)->get(); // cache hit
Rest::assertSentCount(2); // page1 + page2 — page1 repeat was served from cache
Rest::restore();
Model::setCacheStore(null);
class Post extends Model
{
// FK filter: GET comments?postId=1
public function comments(): \Sanchescom\Rest\Relations\HasMany
{
return $this->hasMany(Comment::class);
}
// Nested URL: GET posts/1/thumbnail
public function thumbnail(): \Sanchescom\Rest\Relations\HasOne
{
return $this->hasOne(Thumbnail::class)->nested();
}
// Parent lookup: GET users/{post->userId}
public function author(): \Sanchescom\Rest\Relations\BelongsTo
{
return $this->belongsTo(User::class);
}
}
$post = Post::get(1);
$post->comments; // Collection<Comment> — loaded once, cached
$post->thumbnail; // ?Thumbnail
$post->author; // ?User
// Relations also chain onto the builder:
$post->comments()->where('approved', true)->get();
public function comments(): HasMany
{
return $this->hasMany(Comment::class)->batch(); // GET comments?postId=1,2,3
}
public function author(): BelongsTo
{
return $this->belongsTo(User::class)->batch(); // GET users?id=7,8
}
// Bearer token
'auth' => ['driver' => 'bearer', 'token' => env('API_TOKEN')],
// HTTP Basic
'auth' => ['driver' => 'basic', 'username' => '…', 'password' => '…'],
// Arbitrary header(s) — name must match what the API expects exactly
'auth' => [
'driver' => 'header',
'headers' => ['X-API-Key' => env('API_KEY')],
],
// Custom driver — any class implementing AuthInterface
'auth' => ['driver' => \App\Auth\HmacAuth::class, 'secret' => env('HMAC_SECRET')],
'retry' => [
'times' => 3, // max attempts (excluding the first)
'delay' => 100, // base delay in ms
'multiplier' => 2.0, // exponential multiplier
'statuses' => [429, 500, 502, 503, 504],
'respect_retry_after' => true, // honour Retry-After response header
],
use Illuminate\Events\Dispatcher;
Model::setEventDispatcher(app(Dispatcher::class));
use Sanchescom\Rest\Rest;
Rest::fake([
'posts/*' => Rest::response(['id' => 1, 'title' => 'Hello']),
'posts' => Rest::response(['id' => 2, 'title' => 'Created']),
]);
$post = Post::get(1); // GET posts/1 -> {'id': 1, ...} from the fake
Post::post(['title' => 'Created']); // POST posts -> {'id': 2, ...} from the fake
Rest::assertSentCount(2);
Rest::assertSent(function ($request) {
return $request->method() === 'GET' && $request->uri() === 'posts/1';
});
Rest::assertNotSent(function ($request) {
return $request->method() === 'DELETE';
});
// Inspect all recorded requests
$requests = Rest::recorded(); // list<RecordedRequest>
// Restore the real resolver in teardown
Rest::restore();
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Sanchescom\Rest\Clients\GuzzleClient;
config()->set('rest.default', 'testing');
config()->set('rest.clients.testing', ['provider' => 'mock']);
app('rest')->extend('mock', function () {
$mock = new MockHandler([
new Response(200, [], '{"data":[{"id":1}]}'),
]);
return new GuzzleClient(new Client([
'handler' => HandlerStack::create($mock),
'base_uri' => 'https://api.test/',
'http_errors' => false,
]));
});
$users = User::get(); // served from the mock
use Sanchescom\Rest\ClientResolver;
use Sanchescom\Rest\Clients\GuzzleClient;
use Sanchescom\Rest\Model;
$resolver = new ClientResolver([
'placeholder' => GuzzleClient::fromConfig([
'base_uri' => 'https://jsonplaceholder.typicode.com/',
'options' => ['headers' => ['Accept' => 'application/json']],
]),
]);
$resolver->setDefaultClient('placeholder');
Model::setClientResolver($resolver);
Post::get(1); // works — no Laravel container involved
bash
php artisan vendor:publish --tag=rest-config
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.