Generally, we use the same conventional pattern for our Web and API routes for our models. Let’s see, how to create a reusable trait that generates the model’s route attributes and how to append them in the JSON or array form of it.
The URL Scheme
As a level zero, we can agree on using the RESTful URI scheme. That means every model has an associated route. We can perform different actions on the model, depending on the request type. Let’s see some examples:
// View GET /posts/{id} // Update PATCH /posts/{id} // Delete DELETE /posts/{id}
It can be a bit painful to concatenate the strings and IDs all the time, so we could append this URL as an attribute of the model. That means we could calculate the URL behind the scenes, and use it on the PHP or the JS side as well. For example some usage:
// PHP $post->web_route // /posts/1 // JS post.api_route // /api/posts/1
Then only difference (in this simple example) between the API and the web routes is that the API routes have a /api prefix in the URL.
The Routable Trait
First, let’s clear why do we use a trait here. Since we may use the same pattern for our models, we should extract the logic in a reusable concern, which is a trait in our case. Move on and compose the trait itself:
The API route is not more than the web route with a prefix, so we should start by generating the web route first. The base formula is very easy: the pluralized name of the model + the model ID.
// app/Concerns/Routable.php <?php namespace App\Concerns; use ReflectionClass; trait Routable { /** * Get the api route. * * @return string */ public function getApiRouteAttribute() { return $this->id ? url('api', $this->baseRoute()) : null; } /** * Get the web route. * * @return string */ public function getWebRouteAttribute() { return $this->id ? url($this->baseRoute()) : null; } /** * Get the base route. * * @return string */ protected function baseRoute() { return sprintf('%s/%s', strtolower(str_plural((new ReflectionClass($this))->getShortName())), $this->id ); } }
So, what’s going on here? First of all, we calculate the base route. It consists of the pluralized name of the model and its ID. For example products/2.
Then we generate both the web and the API endpoints for the model. By using the url() helper, we Laravel appends the applications’ URL for the string. If we pass more parameters, then they will be imploded by a /.
Appending the Attributes to the Model’s JSON/Array Form
We can use these computed attributes from PHP now, but if we want to use them in JS, for example, we need to append these to the JSON or array form of the model. Fortunately, we easily can do this with the $appends eloquent property.
<?php namespace App; use App\Concerns\Routable; use Illuminate\Database\Eloquent\Model; class Post extends Model { use Routable; /** * The accessors to append to the model's array form. * * @var array */ protected $appends = [ 'api_route', 'web_route', ]; // ... }
Summary
It’s a more clean approach to use a model’s endpoint than to concatenating strings and IDs in PHP or JS. Of course, you may customize the logic inside the route generators, but it can be a nice starting point.