I'm working inside a Laravel 9 project and am switching over my controller methods to scope the resources. For example, I have an Affiliate model where I'm grabbing the company ID and affiliate ID from the function, and then querying these.
I'm now trying to switch it over to defining the model in the function argument, but aren't sure how to parse company_id through to Affiliate?
This is the previous controller method:
/**
* Display the specified resource.
*
* #return \Illuminate\Http\Response
*/
public function show($company_id, $id)
{
$this->authorize('view', Affiliate::class);
$affiliate = Affiliate::where('company_id', $company_id)
->where('id', $id)
->first();
if (!$affiliate) {
return new ApiSuccessResponse(null, [
'message' => 'Affiliate not found or invalid affiliate ID.'
], 404);
}
return new ApiSuccessResponse($affiliate);
}
And here's my progress so far, how do I add the company_id check to the affiliate?
/**
* Display the specified resource.
*
* #return \Illuminate\Http\Response
*/
public function show($company_id, Affiliate $affiliate)
{
$this->authorize('view', $affiliate);
return new ApiSuccessResponse($affiliate);
}
My route looks like:
/api/company/1000/affiliates/2
And my api.php file:
Route::apiResource('company.affiliates', AffiliateController::class);
Related
I'm working in a Laravel 9 project. I have a Buyer model which has a relationship called tiers, I need to load the count on here.
Right now, I'm doing it like:
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show(Company $company, Buyer $buyer)
{
$this->authorize('view', Buyer::class);
$buyer = Buyer::where('company_id', $company->id)
->where('id', $buyer->id)
->withCount('tiers')
->first();
if (!$buyer) {
return new ApiSuccessResponse(null, [
'message' => 'Buyer not found or invalid buyer ID.'
], 404);
}
return new ApiSuccessResponse($buyer);
}
There must be a better way since the buyer instance is already defined and working as the function argument.
Something like...
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show(Company $company, Buyer $buyer)
{
$this->authorize('view', Buyer::class);
$buyer = $buyer->withCount('tiers');
return new ApiSuccessResponse($buyer);
}
Why doesn't this work and what do I need to change to get it to work?
Laravel has deferred loading for counts, so all you need to do is use loadCount instead of withCount, and there's no need to reassign the variable:
public function show(Company $company, Buyer $buyer)
{
$this->authorize('view', Buyer::class);
$buyer->loadCount('tiers');
return new ApiSuccessResponse($buyer);
}
I have a relationship between work 'days' and projects of different types. So my 'days' record has a reference to my 'projects' table twice because I have two different types of projects called 'Series' and 'Event'.
In my 'days' resource I've created two fields as such:
BelongsTo::make('Series','series',Project::class)->sortable()->nullable(),
BelongsTo::make('Event','event',Project::class)->sortable()->nullable(),
What I'm trying to do is filter the projects by their types so I've created this:
public static function relatableProjects(NovaRequest $request, $query){
return $query->where('type', 'Series');
}
I've tried making relatableSeries and relatableEvents but they don't work. How can I make this connect to the fields correctly without having to create two separate tables for 'series' and 'events'.
The relatableQuery above winds up filtering both resource fields.
Because relatableQuery() is referencing a relatableModel() (so relatableProjects() references the Project model) I was able to create another model solely for the purpose of helping with this.
I created an Event model which references the same projects table and then was able to create a relatableEvents() method to use the where() filter query.
Note: I did have to also create an Event resource which references the Event model since this is how Nova works but was able to hide it from being accessed which you can find more information about here
See revised BelongsTo fields and new model below:
Day resource
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* #param \Laravel\Nova\Http\Requests\NovaRequest $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableProjects(NovaRequest $request, $query){
return $query->where('type', 'Series');
}
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* #param \Laravel\Nova\Http\Requests\NovaRequest $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableEvents(NovaRequest $request, $query){
return $query->where('type', 'Event');
}
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [
ID::make()->hideFromIndex()->hideFromDetail()->hideWhenUpdating(),
BelongsTo::make('User','user',User::class)->sortable(),
BelongsTo::make('Budget','budget',Budget::class)->sortable()->nullable(),
BelongsTo::make('Series','series',Project::class)->sortable()->nullable(),
BelongsTo::make('Event','event',Event::class)->sortable()->nullable(),
DateTime::make('Last Updated','updated_at')->hideFromIndex()->readOnly(),
new Panel('Schedule',$this->schedule()),
new Panel('Time Entry',$this->timeEntries()),
];
}
Event model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'projects';
protected $casts = [
'starts_on' => 'date',
'ends_on' => 'date',
];
public function event(){
return $this->belongsTo('App\Event');
}
public function project(){
return $this->belongsTo('App\Project');
}
}
i know it is an old question but i was facing same problem with laravel nova belongsto field, in some resource i have a belongsto that relates to users but these should be of 'supervisor' role in another resource i hace a belongsto field relating to users but these should be of role 'guard', as laravel nova belongsto field just takes all users in both selects all users appeared and it seems nova belongsto field doe not have a way, or at least i did not find it to scope the query, so what i did was creating a php class named BelongstoScoped this class extends laravel nova field BelongsTo so i overwrote the method responsible of creating the query
<?php
namespace App\Nova\Customized;
use Laravel\Nova\Query\Builder;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Http\Requests\NovaRequest;
class BelongsToScoped extends BelongsTo
{
private $modelScopes = [];
//original function in laravel belongsto field
public function buildAssociatableQuery(NovaRequest $request, $withTrashed = false)
{
$model = forward_static_call(
[$resourceClass = $this->resourceClass, 'newModel']
);
$query = new Builder($resourceClass);
//here i chaned this:
/*
$query->search(
$request, $model->newQuery(), $request->search,
[], [], ''
);
*/
//To this:
/*
$query->search(
$request, $this->addScopesToQuery($model->newQuery()), $request->search,
[], [], ''
);
*/
//The method search receives a query builder as second parameter, i just passed the result of custom function
//addScopesToQuery as second parameter, thi method returns the same query but with the model scopes passed
$request->first === 'true'
? $query->whereKey($model->newQueryWithoutScopes(), $request->current)
: $query->search(
$request, $this->addScopesToQuery($model->newQuery()), $request->search,
[], [], ''
);
return $query->tap(function ($query) use ($request, $model) {
forward_static_call($this->associatableQueryCallable($request, $model), $request, $query, $this);
});
}
//this method reads the property $modelScopes and adds them to the query
private function addScopesToQuery($query){
foreach($this->modelScopes as $scope){
$query->$scope();
}
return $query;
}
// this method should be chained tho the field
//example: BelongsToScoped::make('Supervisores', 'supervisor', 'App\Nova\Users')->scopes(['supervisor', 'active'])
public function scopes(Array $modelScopes){
$this->modelScopes = $modelScopes;
return $this;
}
}
?>
In my users model i have the scopes for supervisors and guard roles like this:
public function scopeActive($query)
{
return $query->where('state', 1);
}
public function scopeSupervisor($query)
{
return $query->role('supervisor');
}
public function scopeSuperadmin($query)
{
return $query->role('superadmin');
}
public function scopeGuarda($query)
{
return $query->role('guarda');
}
So in the laravel nova resource i just included the use of this class
*remember the namespace depends on how you name your file, in my case i created the folder Customized and included the file there:
use App\Nova\Customized\BelongsToScoped;
In the fields in nova resource i used like this:
BelongsToScoped::make('Supervisor', 'supervisorUser', 'App\Nova\Users\User')
->scopes(['supervisor', 'active'])
->searchable()
So that way i could call the belongsto field in the nova resources which filter users depending on modle scopes.
I hope this helps someone, sorry if my English is not that good.
I'm attempting to build a blog like application with the latest version of laravel. I'm trying to figure out how to pull a slug from the database for each article and then route it to all work correctly.
I've got it kind of working but the content wont display on the article if you use the slug to view it.
localhost/articles/1 - works fine, content shows on the page (title etc)
localhost/articles/installing-example - this works but the content errors
This happens when you try to navigate to the page using the slug from the database: Trying to get property 'title' of non-object (View: C:\xampp\htdocs\blogtest\resources\views\articles\show.blade.php)
Error with this line: <h1><?php echo e($articles->title); ?></h1>
app/http/controllers/ArticlesController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Article;
class ArticlesController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$articles = Article::all();
return view('articles.index')->with('articles', $articles);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$articles = Article::find($id);
return view('articles.show')->with('articles', $articles);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
app/Article.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $table = 'articles';
public $primaryKey = 'id';
public $timestamps = true;
}
routes/web.php
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::resource('articles', 'ArticlesController');
resources\views\articles\show.blade.php
#extends('layouts.master')
#section('content')
<h1>{{$articles->title}}</h1>
#endsection
database
Any help and suggestions will be appreciated thanks.
Trying to get property 'title' of non-object (View: C:\xampp\htdocs\blogtest\resources\views\articles\show.blade.php)
Implies that that the $articles is not an object, if you dump it, it should be outputting null - quite rightly so.
The find function is to be used to find a row using your primary key and your primary key is not your slug column, therefore it cannot find a record.
You need to setup a route to accept a {slug} and then based on the slug you need to make the following change:
$articles = Article::find($id);
To,
$articles = Article::where('slug', $slug)->first();
and ensure that $slug is the actual slug and not the id column value.
You need to set route key in your model
// Article model
public function getRouteKeyName(){
return 'slug';
}
Try this
Without slug
Route
Route::resource('articles', 'ArticlesController');
/article/{id}
Controller
public function show($id)
{
$articles = Article::first($id);
return view('articles.show', compact('articles'));
}
Blade
#extends('layouts.master')
#section('content')
#if(!is_null($articles))
<h1>{{$articles->title}}</h1>
#endif
#endsection
With Slug
Route
Route::get('/articles/{slug}', ['as'=>'articles.by.slug', 'uses'=> 'ArticlesController#showArticles']);
or
Route::get('/articles/{slug}', 'ArticlesController#showArticles')->name('articles.by.slug');
/article/{slug}
Controller
public function showArticles($slug)
{
$articles = Article::where('slug', $slug)->first();
return view('articles.show', compact('articles'));
}
Blade
#extends('layouts.master')
#section('content')
#if(!is_null($articles)) //or #if($articles)
<h1>{{$articles->title}}</h1>
#endif
#endsection
This Is because you getting null data from database.
Reason is, you passing slug in the id field so
localhost/articles/installing-example
you passing id = installing-example, hence no id is found so no data you get.
so for this
convert your slud into original form and search not with id, search with your slug field.
let suppose from the title you make slug.
title is installing example
so when you get slug, make it into original form
then search with original like
$articles = Article::where('titile',$original)->first();
I am building a web app + REST server with Laravel 5.5 so that the users can either access the services online with a web interface or indirectly use the APIs via the mobile app.
Now the objective would be to have the same controllers capable of handling both API and direct requests leveraging on Laravel built-in double routing and automatic JSON responses for FormRequests.
The main problems I am figuring are:
How to handle JSON (for API access) and HTML view (for web visitors) responses on the same controller function?
How to manage "Resource not found" errors in the controller and subsequently reply to the user in the desired way?
A possible approach to the second issue would be to use "findOrFail" and then catch the exception, looking whether the request has got an "Accpet" header and reply accordingly but it looks quite bulky.
Here is a brief overview of a controller I am working on; I haven't implemented any checks on the retrieved data yet.
class UsersController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$users = User::all();
return UserResource::collection($users);
}
/**
* Store a newly created resource in storage.
*
* #param \Washery\Http\Request\StoreUser $request
* #return \Illuminate\Http\Response
*/
public function store(StoreUser $request)
{
User::create($request->all());
return response()->json(['message' => 'success'], 200);
}
/**
* Display the specified resource.
*
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$user = User::find($id);
return new UserResource($user);
}
/**
* Update the specified resource in storage.
*
* #param \Washery\Http\Request\UpdateUser $request
* #return \Illuminate\Http\Response
*/
public function update(UpdateUser $request)
{
User::update($request->all());
return response()->json(['message' => 'success'], 200);
}
/**
* Remove the specified resource from storage.
*
* #param \Washery\User $user
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
User::find($id)->delete();
return response()->json(['message' => 'success'], 200);
}
}
An approach would be to know where the request is coming from. If it comes from the mobile (API request), then return JSON, else, return a view.
if ($request->expectsJson()) {
return response()->json(['message' => 'success']); // No need to put 200 here.
} else {
return view('view.path');
}
You can learn more about the request api here: https://laravel.com/api/5.5/Illuminate/Http/Request.html
I get the error when trying to make a post call to /api/subject/search
I assume it's a simple syntax error I'm missing
I have my api routes defined below
Route::group(array('prefix' => 'api'), function()
{
Route::post('resource/search', 'ResourceController');
Route::resource('resource', 'ResourceController');
Route::post('subject/search', 'SubjectController');
Route::resource('subject', 'SubjectController');
Route::resource('user', 'UserController');
Route::controller('/session', 'SessionController');
Route::post('/login', array('as' => 'session', 'uses' => 'SessionController#Store'));
});
And my controller is mostly empty
class SubjectController extends \BaseController
{
public function search()
{
$subjects = [];
if((int)Input::get('grade_id') < 13 && (int)Input::get('grade_id') > 8)
$subjects = Subject::where('name', 'like', '%HS%')->get();
else
$subjects = Subject::where('name', 'not like', '%HS%')->get();
return Response::json([
'success' => true,
'subjects' => $subjects->toArray()
]);
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
}
}
You need to specify the method.
try
Route::post('subject/search', 'SubjectController#search');
See the named route example:
Laravel Docs
In your case I think search is not resolved by the controller to load the search() method. You are also sending a POST for search functionality and I guess it's better to do a GET request since POST and PUT are for storing data.
Conventions
When creating API's it's a good thing to stick to naming conventions and patterns.
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
Solution
Your route could be simpler like this: api.yourdomain.com/api/subject?search=term1,term2. Doing this with a GET query makes it going to the index() method. There you can check the GET params and do your search stuff and return.
Check this for the cleanest and truely RESTful way to make an API in Laravel:
How do I create a RESTful API in Laravel to use in my BackboneJS app
I got same error when accessing object at index of an empty array in view blade php file.