I am working on a API project which uses Laravel 11. As mentioned when I send a PUT request on a certain endpoints I am getting the error:
"No query results for model"
Below you can find routes in the api.php code:
Route::prefix('area')->middleware('client')->group(function () {
Route::delete('country', [CountryController::class, 'destroyMultiple']);
Route::delete('city', [CityController::class, 'destroyMultiple']);
Route::delete('district', [DistrictController::class, 'destroyMultiple']);
Route::delete('neighborhood', [NeighborhoodController::class, 'destroyMultiple']);
Route::apiResource('country', CountryController::class);
Route::apiResource('city', CityController::class);
Route::apiResource('district', DistrictController::class);
Route::apiResource('neighborhood', NeighborhoodController::class);
});
I get this error for every PUT route except CountryController
PUT route. Firstly this is a working endpoint in CountryController
:
public function update(UpdateCountryRequest $request, Country $country)
{
DB::transaction(function () use ($request, $country) {
$country->update($request->only(['country_abbreviation', 'country_code', 'logo', 'status']));
$delete = [];
foreach ($request->languages as $language) {
$countryName = $country->CountryNames()->updateOrCreate([
'lang_id' => $language['lang_id'],
'country_id' => $country->id,
], $language);
$delete[] = $countryName->id;
}
CountryLanguage::where('country_id', $country->id)->whereNotIn('id', $delete)->delete();
});
return response()->json([
'message' => 'Country updated successfully',
'status' => 'success',
]);
}
And also the UpdateCountryRequest
file as below:
public function authorize(): bool
{
if ($this->user()->isAdmin) {
return true;
}
return false;
}
public function rules(): array
{
$country = $this->route('country');
return [
'country_abbreviation' => ['required', 'string', Rule::unique('countries')->ignore($country->id)], // keeping the same country_abbreviation seperately
'country_code' => ['required', 'string', 'max:5', Rule::unique('countries')->ignore($country->id)],
'logo' => ['required', 'string', 'max:255'],
'status' => ['required', 'boolean'],
'languages' => ['required', 'array'],
'languages.*.lang_id' => ['required', 'integer', 'exists:languages,id'],
'languages.*.country_name' => ['required', 'string'],
];
}
And Postman PUT Request:
But for other controllers when I send PUT request I get the mentioned error:
"No query results for model"
For example let me give you the same codes for DistrictController
:
Update metot inside controller file:
public function update(UpdateDistrictRequest $request, District $district)
{
DB::transaction(function () use ($request, $district) {
$district->update($request->validated());
});
return response()->json(['message' => 'District updated successfully'], 200);
}
UpdateDistrictRequest
code:
public function authorize(): bool
{
if ($this->user()->isAdmin) {
return true;
}
return false;
}
public function rules(): array
{
return [
'service_id' => 'required|integer',
'city_id' => 'required|integer|exists:cities,id',
'name' => 'required|string|max:255',
'status' => 'required|boolean',
];
}
And also the Postman PUT request:
I have checked if district exist with given id and it exist on database.
Below you can find migrations:
Country:
Schema::create('countries', function (Blueprint $table) {
$table->id();
$table->string('country_abbreviation');
$table->string('country_code');
$table->string('logo');
$table->tinyInteger('status');
$table->timestamps();
});
District:
Schema::create('districts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('service_id')->nullable();
$table->unsignedBigInteger('city_id');
$table->foreign('city_id')->on('cities')->references('id')->onDelete('cascade');
$table->string('name');
$table->boolean('status')->default(1);
$table->timestamps();
});
And also models:
Country:
use HasFactory;
protected $guarded = [];
protected $fillable = [
'country_abbreviation',
'country_code',
'logo',
'status'
];
// ... Relationship methods
District:
protected $guarded = [];
protected $fillable = [
'service_id',
'city_id',
'name',
'status'
];
// ... Relationship methods
When I run "route:list" method with artisan I can see the all put endpoints listed properly.
I have tried changing route definitions inside api.php. For example moving DistrictCotroller
line to the top of it but it stills gives same error at the same time when I do that CountryController
request still works properly.
The exception is throwned by this method inside src > Illuminate > Routing > ImlicitRouteBinding.php
public static function resolveForRoute($container, $route)
{
$parameters = $route->parameters();
$route = static::resolveBackedEnumsForRoute($route, $parameters);
foreach ($route->signatureParameters(['subClass' => UrlRoutable::class]) as $parameter) {
if (!$parameterName = static::getParameterName($parameter->getName(), $parameters)) {
continue;
}
$parameterValue = $parameters[$parameterName];
if ($parameterValue instanceof UrlRoutable) {
continue;
}
$instance = $container->make(Reflector::getParameterClassName($parameter));
$parent = $route->parentOfParameter($parameterName);
$routeBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance))
? 'resolveSoftDeletableRouteBinding'
: 'resolveRouteBinding';
if (
$parent instanceof UrlRoutable &&
!$route->preventsScopedBindings() &&
($route->enforcesScopedBindings() || array_key_exists($parameterName, $route->bindingFields()))
) {
$childRouteBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance))
? 'resolveSoftDeletableChildRouteBinding'
: 'resolveChildRouteBinding';
if (
!$model = $parent->{$childRouteBindingMethod}(
$parameterName,
$parameterValue,
$route->bindingFieldFor($parameterName)
)
) {
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
}
} elseif (!$model = $instance->{$routeBindingMethod}($parameterValue, $route->bindingFieldFor($parameterName))) {
// EXCEPTION
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
}
$route->setParameter($parameterName, $model);
}
}
I changed update methods in both CountryController
and DistrictController
as below and noticed that on DistrictController
, querying database is just not working.
// CountryController update
public function update(UpdateCountryRequest $request, int $id)
{
dd($id, District::find($id)); // 1, District {#1444 ...}
// DistrictController update
public function update(UpdateDistrictRequest $request, int $id)
{
dd($id, District::find($id)); // 1, null
I double checked the namespaces in both controllers and they are identical.
It seems problem caused by Global Scopes
which I've overlooked. On our models we have boot
functions as below for registering Global Scopes
to service container I believe :
// District Model
protected static function boot()
{
parent::boot();
static::addGlobalScope(new FilterBy('App\Services\V1\Area\DistrictFilters', request()->all()));
}
Also below you can find FilterBy
scope file codes :
class FilterBy implements Scope
{
protected $namespace;
protected $filters;
public function __construct($namespace, $filters)
{
$this->namespace = $namespace;
$this->filters = $filters;
}
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
$filter = new FilterBuilder($builder, $this->filters, $this->namespace);
$builder = $filter->apply();
}
}
And also in FilterBuilder
class we are applying filters as below :
public function apply()
{
foreach ($this->filters as $name => $value) {
$normailizedName = ucfirst($name);
$class = $this->namespace . "\\{$normailizedName}";
if (! class_exists($class)) {
continue;
}
if (strlen($value)) {
(new $class($this->query))->handle($value);
} else {
(new $class($this->query))->handle();
}
}
return $this->query;
}
And finally we have Name.php
and CityId.php
files inside the given App\Services\V1\Area\DistrictFilters
namespace. They have handle
methods inside them like below :
public function handle($value = ""): void
{
// Name.php
$this->query->where('name', 'ilike','%'. $value .'%');
}
So If I understand it correctly, when I send PUT
request to let's say DistrictController for example, because of these QueryFilters
unless I send the 'Name
' field as it's saved in database, it just can't find the model.
For example I created a Distrcit
record which has an Id with 974 as below :
If I send a PUT request as below with same 'Name
', it manages to find the Model and updates it :
{
"service_id" : 1,
"city_id" : 1,
"name" : "District",
"status" : 1
}
But If i send the same request with updated name something like 'District 123
' it can't find Model.
So I updated the apply
function inside FilterBy
as below to prevent adding QueryFilters
and this seems solved the problem :
public function apply(Builder $builder, Model $model): void
{
if (collect(['PUT', 'PATCH'])->contains(request()->method())) {
return;
}
$filter = new FilterBuilder($builder, $this->filters, $this->namespace);
$builder = $filter->apply();
}
It would be faster to find the problem if I have shared the whole District
Model code. It's my bad. Thank you everyone for trying to help.
We encountered an error like this in previous projects. If you are using Scope, these scopes may affect the put and patch select queries.
To see a preview of the queries;
//DistrictController
public function update(UpdateDistrictRequest $request, int $id)
{
District::dd();
If the problem is caused by scopes, you can skip scopes for put and patch request methods.