Call to a member function first() on null in laravel resource - laravel

i try to build a customized response in my resource like this:
class ApplicationResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'sort'=> $this->sort,
'is_seen' => $this->is_seen,
'name' => $this->name,
'position' => $this->position,
'company' => $this->company,
'education' => $this->education,
'degree' => $this->degree,
'phone' => $this->phone,
'university' => $this->university,
'cv_folder_id' => $this->cv_folder_id,
'cv' => route('applications.cvShow', ['candidateCv' => $this->candidate_cv]),
'comments'=> ApplicationCommentsResource::collection($this->applicationComments),
'ratingFields'=> ApplicationRatingsResource::collection($this->applicationRatings()->get()),
'jobPostRatingFields' => JobPostRatingFieldsResource::collection($this->jobPost->jobPostRatingFields),
];
}
}
but i just get errors. the error i get is:
Call to a member function first() on null
i dont know how to build my response that if the collection is empty i dont get any error?

That simply means that you want to retrieve value that does not exist.
You can make simple condition like that:
if(is_null($this->sort)){
return "-";
}
Good luck!

I'm pretty sure the relationship is the problem.
But since there is not enough information, first of all find out in which line the error is, then check the relationships.
For example:
'comments'=> ApplicationCommentsResource::collection($this->applicationComments)
Model Application must have relationship applicationComments

Related

laravel endpoint hide field

How can i hide some fields ?
i want to hide the file field
Eloquent :
$reports = Report::select('id', 'file','company_id', 'title', 'year', 'created_at')
->orderBy('id', 'desc')
->paginate(10);
return ReportResource::collection($reports);
Model :
...
public function getFileSizeAttribute()
{
return Storage::disk('files')->size($this->attributes['file']);
}
....
ReportResource:
...
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'year' => $this->year,
'views' => $this->whenNotNull($this->views),
'file' => $this->whenNotNull($this->file), <-- i want to hide the file field
'file_size' => $this->fileSize, <-- but always show file_size
'created_at' => $this->created_at,
'company' => new CompanyResource($this->company),
];
}
to get file_size field i must select the file field , because it's depends on it to calculate the file size.
but i want to hide the file field before send the response.
i know i can use the protected $hidden = [] method in the model , but i don't want that, because file field it's required on others place. i just want to hide it on this endpoint only.
Since you are using API resources the best and clean way to do this is by using a Resource class for your collection.
Said that, you will have 3 Resources:
The first one, as it is, just for retrieving a single Model with file and file_size attributes. The one you already have ReportResource.php
...
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'year' => $this->year,
'views' => $this->whenNotNull($this->views),
'file' => $this->whenNotNull($this->file),
'file_size' => $this->fileSize,
'created_at' => $this->created_at,
'company' => new CompanyResource($this->company),
];
}
A new second resource to be used in your endpoint, without the file attribute. IE: ReportIndexResource.php
...
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'year' => $this->year,
'views' => $this->whenNotNull($this->views),
'file_size' => $this->fileSize,
'created_at' => $this->created_at,
'company' => new CompanyResource($this->company),
];
}
Now you need to create a Resource collection which explicitly defines the Model Resource to use. IE: ReportCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ReportCollection extends ResourceCollection
{
/**
* The resource that this resource collects.
*
* #var string
*/
public $collects = ReportIndexResource::class;
}
Finally, use this new resource collection in your endpoint
$reports = Report::select('id', 'file','company_id', 'title', 'year', 'created_at')
->orderBy('id', 'desc')
->paginate(10);
return new ReportCollection($reports);
Of course, you can make use of makeHidden() method, but IMO is better to write a little more code and avoid a non desired attribute in your response because you forgot to make it hidden.
Also, in case you make use of makeHidden() method and you want to show the attribute in a future, you will have to update all your queries instead of a silgle resource file.
If you want to make it Hide From All Returns , you can Do this in model
protected $hidden = ['file'];
and if you want to do it temporirly with this query , you can Use MakeHidden method
$users = $reports->makeHidden(['file']);
It's clear in laravel docs , take a look
https://laravel.com/docs/9.x/eloquent-collections#method-makeHidden

How to specify only methods for resource in array Laravel

In web.php i have this Route::resources method
Route::resources([
'products' => App\Http\Controllers\ProductsController::class,
'categories' => App\Http\Controllers\CategoriesController::class,
'page-info' => App\Http\Controllers\PageInfoController::class,
]);
How can I specify only index,edit and update for 'page-info' route? and how to add name to each route?
I know that I can do it like this
Route::resource('products', ProductsController::class);
Route::resource('page-info', PageInfoController::class)->only([
'index', 'edit', 'update'
]);
//itd...
but I like the array one resources and thought it is possible too
You can't do what you want, if the documentation does not explicitly say you can or not, you can always have a look at the source code.
So, source code for Laravel 8.x has:
/**
* Register an array of resource controllers.
*
* #param array $resources
* #param array $options
* #return void
*/
public function resources(array $resources, array $options = [])
{
foreach ($resources as $name => $controller) {
$this->resource($name, $controller, $options);
}
}
So, you can do
Route::resources([
'products' => App\Http\Controllers\ProductsController::class,
'categories' => App\Http\Controllers\CategoriesController::class,
'page-info' => App\Http\Controllers\PageInfoController::class,
], $options);
But the code shows that it will share the options for all the resources to be created, so the answer is a no, you can't (at least what I am looking at the source code).

Validating not all fields in form - Laravel

In laravel, I have created a form. At the moment, I am working on the validation of the input fields of this form. I ran into a problem when I tried to validate some input fields and others not. For example, mail should be validated but catering_name not (it isn't necessary to fill in this field, its an option)
I have tried all validation methods I could find. I keep getting the same error.
Method Illuminate\Validation\Validator::validatePhone does not exist.
I guess I am missing something.
I have tried:
Validator::make($request->...
$this->validate(request(), [ ...
$request->validate([ ...
Bellow, you will find all the data that should be inputted in the database.
If I remove the validation part, the data got inserted into the database. I think the problem lays with how I try to validate. Thanks for any help.
$this->validate(request(), [
'add_name' => 'required|min:3',
'add_mail' => 'required|email',
'name' => 'required|min:3',
'email' => 'required|email',
'telefone' => 'numeric|phone',
'gsm' => 'numeric|phone',
'event' => 'required|min:3',
'date_start' => 'required|date|after:tomorrow',
'date_end' => 'required|date|after_or_equal:event_date_start',
'location' => 'required|min:3',
'number' => 'required',
]);
$event = new Event;
$event->add_name = request('add_name');
$event->add_mail = request('add_mail');
$event->name = request('name');
$event->email = request('email');
$event->telefone = request('telefone');
$event->gsm = request('gsm');
$event->name = request('name');
$event->date_start = request('date_start');
$event->date_end = request('date_end');
$event->location = request('location');
$event->number = request('number');
$event->catering = request('catering');
$event->catering_name = request('catering_name');
$event->remarks = request('remarks');
$event->status = Event::STATUS_0;
$event->save();
Unfortunately phone is not one of the default validation. You can try something like:
[
'telefone' => 'required|regex:/(01)[0-9]{9}/',
]
You can see the available list of validations given by Laravel here.
There are a wide variety of more complex options depending on how important it is to you.
There are packages for easy plug and play like Laravel-Phone.
You can create your own custom validation using php artisan make:rule phone_number and then editing the new rule made:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class PhoneNumber implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
// logic here, most likely some sort of regex.
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute must be a valid phone number.';
}
}

Laravel Resources many different requests for the same Resource instance

I'm using Laravel Resource as a tool to sending API data. However, when I make just a basic controller with CRUD methods - all methods are different, for example
public function show(Offer $offer)
{
return $offer;
}
public function index()
{
return Offer::orderBy('created_at','desc')->get();
}
So I created OfferResource and for show method it's:
return new OfferResource($offer);
class OfferResource extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'body' => $this->body,
'title' => $this->title,
'specialities' => $this->specialities()->pluck('speciality_id'),
'specialities_name' => $this->specialities()->pluck('name')
];
}
}
how should I call OfferResource for method index()?
Should I create another Resource "Controller"? or is there a different approach to use the same OfferResource?
You can use collection() method to use this resource for index -
return OfferResource::collection(Offer::orderBy('created_at','desc')->get());
And it is best to use a resource for multiple purpose if you can use rather than creating new one for each purpose.
As we all know the concepts of code re-use, the very basic foundation of OOP, we want to write ones but use multiple times. That's why we write Resource class to use whenever we need to transform our data to a array in predefined format. And requirement always change, so it would be much better to use resource to keep up with changes.
A resource class represents a single model that needs to be transformed into a JSON structure.
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
Laravel also provide a way to use this method to transform a collection of resources using collection() method.

Laravel Validation unique/exists with different database connection

In the documentation, I saw you could set a connection for the unique rule which is great. However, the exists doesn't seem to follow the same logic. Take this for example:
$rules = [
'username' => 'required|max:40|unique:user',
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:int.user',
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
];
The unique rule works GREAT in this instance. It uses my database connection called 'int' and calls the user table. HOWEVER, when the rules are reversed like so:
$rules['email'] = 'required|email|max:255|exists:int.user';
I got this error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'int.user'
doesn't exist (SQL: select count(*) as aggregate from int.user where
email = haleybuggs6#gmail.com)
It's trying to call an int.user table instead of using the int database connection.
Is there a reason exists doesn't act the same way as unique? Thanks.
instead of using connection name you can try with straight Database name which is defined in "int" connection. faced similar problem and these way worked for me. like
$rules['email'] = 'required|email|max:255|exists:DB_Name.user';
You can use
'email' => 'exists:mysql2.users|required'
Where mysql2 is second database settings array in the database.php file
Try it.
$rules = [
'username' => 'required|max:40|unique:connection_name.user',
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:connection_name.user',
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
];
Ultimately for Laravel 5.6.* you need to look at an existing instance of the model you are trying to validate, or specify ...
{db_connection_name}.{schema_name}.{table_name}
... to ensure that you are looking at the proper table.
Validation Example
validate it...
<?php
// for instance...
// maybe auth user is in a different db
// = so you cannot validate with your default db connection
$default_user = Auth::user();
// pass the instance in order to allow Validator to qualify the proper connection/name
\App\Validation\User::validate($_POST, $default_user);
User Validation class
<?php
namespace App\Validation;
class User extends Validator
{
/**
* #param \Illuminate\Database\Eloquent\Model|string $mixed
* #param string $default
* #return string
*/
public static function table($mixed,$default='default_connection.app_schema.users_table')
{
if($mixed instanceof \Illuminate\Database\Eloquent\Model){
$table = $mixed->getConnectionName().'.'.$mixed->getTable();
} else {
if (! empty($mixed)) {
$table = $mixed;
} else {
$table = $default;
}
}
return $table;
}
/**
* validation to create a new user
*
* #param array $data
* #param \App\User|string $mixed
* #return array
* #throws \Illuminate\Validation\ValidationException
*/
public static function validate(array $data, $mixed='default_connection.app_schema.users_table'){
return Validator::validate($data,[
'username' => 'required|max:40|unique:'.self::table($mixed),
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:'.self::table($mixed),
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
]);
}
}
$default_connection = 'db_name';
$rules = [
'username' => 'required|max:40|unique:{$default_connection}.user',
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:int.user',
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
];

Resources