Implicit route, with firstOrCreate instead of findOrFail - laravel

Is it possible to create an implicit route where if it is not found the thing is created? I am using Laravel 5.5.13.
For instance this is my implicit route:
Route::post('thumbs/{player}', 'ThumbController#store');
And in my controller it is this:
public function store(Request $request, Player $player)
{
$thumb = new Thumb($request->all());
$player->thumbs()->save($thumb);
return response()->json($thumb, 201);
}
So now if I go to the endpoint of ..../api/thumb/1 it will create a thumb related with Player of id 1. However instead of a id number I wanted to provide it a string like this:
..../api/thumb/PLAYER_NAME
So example of ..../api/thumb/Blagoh, then my endpoint should first find if a player exists by name "Blagoh", and if it doesn't then it should create it. I couldn't figure this one out.

What you should do is adding into boot method of RouteServiceProvider something like this:
Route::bind('player', function ($value) {
if ($player = \App\Player::find($value)) {
return $player;
}
return Player::create(['name' => $value]);
});
It's called explicit binding and you can update logic as showed above. Reference - Route model binding

Related

How can I encrypt all ids in URL (laravel 9) using repository pattern

I need to do all url ids encrypted like :
user/edit/1
items/edit/35
posts/details/52
to below url path
user/edit/gd43dfrg
items/edit/sdfg4343
posts/details/fasdf23423
there is lots of areas in repository pattern like UserRepository , UserController blade files and in controllers that id used url ('items/edit/2')
however also in controller some function are passed by objects like
public function itemedit(Items $items)
I tried
$encrypt_val = Crypt::encrypt($value) and $decrypt_val = Crypt::decrypt($encrypt_val );
but I need to do it all over app. There is any short way or Middleware function to do it using repository pattern ?
The proper way to do this would be like so:
Use any boot function of a service provider (e.g. RouteServiceProvider) to define how a route parameter should be invoked:
public function boot()
{
// Bind any `{order}` route parameter such that it decodes the value before retrieving the order.
Route::bind('order', function ($value) {
return User::query()->where('id', $this->yourDecodeFunction($value))->firstOrFail();
});
}
Now when you create urls like route('orders.show', yourEncodeFunction($order->id)) from a route
Route::get('/orders/{order}', /*...*/);
your controller method will receive the expected order in its signature i.e.
public function show(Request $request, Order $order) {
//
}
To improve this code, you can define the getRouteKey() function (part of the UrlRoutable trait) on your Eloquent model to simplify the creation of your routes so that you can call route('orders.show', $order):
// App/Models/Order.php
public function getRouteKey()
{
return yourEncodeFunction($this->getKey());
}
This will make sure that the route parameter is automatically identified when you pass the complete object to the route call (if you don't override this function yourself it uses the model's primary key).
https://packagist.org/packages/hashids/hashids might be a good package for you if you just want to obfuscate some of the url (note that this package is an 'encoder' and not an 'encrypter').

How do I pass a value in my Route to the Controller to be used in the View in Laravel?

I have 2 entities called Match and Roster.
My Match routes are like this
http://localhost:8888/app/public/matches (index)
http://localhost:8888/app/public/matches/14 (show)
In order to view/create the teams for each specific match I added the routes for the match roster like this:
Route::get('/matches/'.'{id}'.'/roster/', [App\Http\Controllers\RosterController::class, 'index']);
Now I need that {id} i have in my URL to pass it to the Controller here:
public function index()
{
return view('roster.index');
}
I need that for a couple of things. First I need to do a search on the Roster table filtering by a column with that value, so I can display only the players that belong to that match.
Second, I need to pass it on to the view so I can use it on my store and update forms. I want to add or remove players from the roster from that same index view.
How can I do that?
#1 You can get the route parameter defined on ur routes via request()->route('parameter_name').
public function index()
{
// get {id} from the route (/matches/{id}/roster)
$id = request()->route('id');
}
#2 You can pass the data object via using return view(file_name, object)
public function index()
{
// get {id} from the route (/matches/{id}/roster)
$id = request()->route('id');
// query what u want to show
// dunno ur models specific things, so just simple example.
$rosters = Roster::where('match_id', '=', $id);
// return view & data
return view('roster.index', $rosters);
}
#3 It can be done not only index but also others (create, store, edit, update)
In addition, STRONGLY RECOMMEND learn Official Tutorial with simple example first.
Like a Blog, Board, etc..
You need to know essentials to build Laravel App.
Most of the time, I prefer named routes.
Route::get('{bundle}/edit', [BundleController::class, 'edit'])->name('bundle.edit');
In controller
public function edit(Bundle $bundle): Response
{
// do your magic here
}
You can call the route by,
route('bundle.edit', $bundle);

The update nor the destroy methods won't work in laravel eloquent model?

I have a strange situation where eloquent model won't let me update nor destroy while index and create is working fine!
I'm using Vue.js and Laravel API Resource for form control, and while it worked with me before, it won't work here:
Here's my Vue.js Code:
updateFinish(finish) {
axios.patch(`/api/finishes/${finish.id}`, finish).then(response => {
this.fetchFinishes();
}).catch(error => {
// Get laravel validation error
this.errors = error.response.data.errors;
});
},
laravel update code (not working)
public function update(FinishType $finishType)
{
// Don't know why not working
$finishType->update($this->validateRequest());
return new FinishTypeResource($finishType);
}
the response is null:
{"id":null,"name":null}
While this code works:
public function update($id)
{
$finishType = FinishType::find($id);
$validates = $this->validateRequest();
$finishType->name = $validates['name'];
$finishType->save();
return new FinishTypeResource($finishType);
}
public function validateRequest()
{
return request()->validate([
'name' => 'required | unique:finish_types',
]);
}
Note the Model name is FinishType and database table name is finish_types, I even tried to define the table name in the model like so protected $table = 'finish_types' – still not working and I already have defined the $fillable array!!!
Your route model binding is not working correctly, for the implicit binding to work your injected variable should match the route parameter name.
Assuming that your parameter name could be finish (reading the url from your javascript) you have to write the update function using $finish as injected variable, like this:
public function update(FinishType $finish)
{
$finish->update($this->validateRequest());
return new FinishTypeResource($finish);
}
Do the same for destroy():
public function destroy(FinishType $finish)
{
// your destroy code here
}
In any case you can run php artisan route:list to find your parameter name (the part of the URI in braces) and give the same name to the injected variable.
If the two do not match, parameter and injected variable name, laravel injects a void, not loaded, FinishType model so it does not make sense doing an update or a delete on it.
I can't post comments so I'm going to post what I assume is the answer.
Laravel does route model binding automagically when the route url name corresponds to the name of the table I think... or model.
So users/{id} would auto bind the User object when you type it as a param in the controller. Example (User $user)
However, since your URL seems to be "different" from the name of your Model/Table, go to the RouteServiceProvider, and manually do the binding.
So in your case you'd do something like this in the boot function of the RouteServiceProvider class:
public function boot()
{
parent::boot();
Route::model('finishes', FinishType::class);
}
Don't forget your imports :)
You can read more about Explicit Model Binding here: https://laravel.com/docs/5.8/routing#explicit-binding

Multiple implicit model binding with one controller in Laravel

I am trying to attach multiple models with one controller using implicit model binding but I am getting the following error if I try to attach more than one model with methods.
index() must be an instance of App\\Http\\Models\\Modelname, string given
Here is my code:
public function index(Model1 $model1,Model2 $model2,Model3 $model3)
{
print_r($application_endpoint);
}
Route:
Route::resource("model1.model2.model3","MyController",["except"=>["create","edit"]]);
Your route should looks like that:
Route::resource("your_route/{model1}/{model2}/{model3}","MyController",[
"except"=>["create","edit"]
]);
Yess you can register routes like these
Route::resource("model1.model2.model3","MyController",["except"=>["create","edit"]]);
but in your controller, you have to
public function index($id,$id2,$id3)
{
print_r($application_endpoint);
}
OR
you can do like this
Route::model('key/key/key', 'MyController')
and in your controller
public function index(Model1 $model1,Model2 $model2,Model3 $model3)
{
print_r($application_endpoint);
}

Laravel Backpack - getting current record from crud controller

In my crud controller I am trying to get the name of the person who is currently being edited.
so
http://192.168.10.10/admin/people/93/edit
In the people crud controller
public function setup() {
dd(\App\Models\People::get()->first()->name)
}
This returns the first person not the person currently being edited.
How do I return the current person (with an id of 93 in this example)
Ok, So since you use backpack look into CrudController to see how the method looks:
public function edit($id)
{
$this->crud->hasAccessOrFail('update');
$this->data['entry'] = $this->crud->getEntry($id);
$this->data['crud'] = $this->crud;
$this->data['fields'] = $this->crud->getUpdateFields($id);
$this->data['id'] = $id;
return view('crud::edit', $this->data);
}
So now you can overwrite the edit function and change whatever you want. You can even create a custom edit page if you so wish.
Setup on the other hand is usually used to add things like
$this->crud->addClause(...);
Or you can even get the entire constructor and put it in the setup method because setup call looks like this:
public function __construct()
{
// call the setup function inside this closure to also have the request there
// this way, developers can use things stored in session (auth variables, etc)
$this->middleware(function ($request, $next) {
$this->setup();
return $next($request);
});
}
So you could do something like \Auth::user()->id;
Also it's normal to work like this. If you only use pure laravel you will only have access to the current id in the routes that you set accordingly.
Rahman said about find($id) method. If you want to abort 404 exception just use method findOrFail($id). In my opinion it's better way, because find($id)->name can throw
"Trying to get property of non-object error ..."
findOrFail($id) first fetch user with specified ID. If doesn't exists just throw 404, not 500.
The best answer is:
public function edit($id)
{
return \App\Models\People::findOrFail($id);
}
Good luck.
you need person against id, try below
public function setup($id) {
dd(\App\Models\People::find($id)->name);
}

Resources