Reputation: 799
is there any way of creating a Polymorphic-like model implicitly binding on laravel 5.* ??
for example:
route('project.comment.store', $project->commentable)
register the route like this
Route::get('comments/{commentable}/store', ....controller . '@store')
and receive the exactly model on the controller store() method?
im guessing the only way i could archive this is if i modify the route to something like this
Route::get('comments/{commentable_type}/{commentable_id}/store', ....controller . '@store')
which looks a big ugly.. but works... any idea if is there any short and smarter way of doing so?
Upvotes: 4
Views: 2048
Reputation: 46
I have found a solution to this problem that is pretty clean. It only requires that you use morph maps and that the route has the same name(or pluralized) as the morph entry.
Route::apiResource('customers.addresses', AddressController::class)
->parameter('customers','morphParent')
->scoped();
Route::bind('morphParent', function($value, RoutingRoute $route) {
$name = str($route->action['as'])->before('.')->singular()->toString();
$model = Relation::getMorphedModel($name);
return $model::findOrFail($value);
});
public function store(Model $addressable, AddressRequest $request): JsonResource
{
...
}
You could make morphGrandParent binding etc too.
Upvotes: 0
Reputation: 401
You can check this
Route::apiResource('projects.comments', [CommentController::class, 'index'])
->parameters([
'projects' => 'commentable',
]);
...and then in controller (without type hint)
public function index($commentable)
Upvotes: 0
Reputation: 3525
Sorry I'm late. You can polymorphic route model bind like this:
Add a Route::bind() to either the RouteServiceProvider or your packages ServiceProvider. Your custom model resolution logic will go there.
Inject a plain old \Illuminate\Database\Eloquent\Model rather than anything specific. (Or whatever base model you extend from).
Here's an example from my existing code... My binding is on /my-model/{key}/seo. During the bind, I custom resolve MyModel from my-model and then use {key} for the id.
// my package routes file
Route::group(['as' => 'fuqu::', 'namespace' => 'FuquIo\\LaravelTags', 'middleware' => ['web', 'auth']], function (){
$models = config(\FuquIo\LaravelTags\ServiceProvider::SHORT_NAME .'.orm');
foreach($models as $short_name => $fqns_class){
$kebbab_name= kebab_case($short_name);
Route::get("/$kebbab_name/{key}/seo", [
'as' => "$kebbab_name.seo",
'uses' => function(\Illuminate\Database\Eloquent\Model $model){
return $model;
}
]);
}
});
// my package ServiceProvider.php (or your RouteServiceProvider.php) in the boot() but you need to use Illuminate\Routing\Route as RouteInfo;
for that injection I'm doing in the bind.
// at the top
use Illuminate\Routing\Route as RouteInfo;
...
// then inject RouteInfo for your binding logic so you can tear apart your url/uri.
Route::bind('key', function ($key, RouteInfo $route) {
$info = explode('/{key}', $route->uri());
$kebab = $info[0];
$orm = studly_case($kebab);
$fqns_class = config(self::SHORT_NAME .'.orm.'. $orm);
return $fqns_class::findOrFail($key);
});
So, in my case I've got a config file mapping SimpleNames => Fully\NameSpaced\Names::class, but you don't need that. You can resolve however you like.
Upvotes: 1