Laravel Mail: attach if not null - laravel

I am sending emails using the mailable class of Laravel. I want to attach files, if the files exist. So I only want to add the attach parameter if the file exists. I could work with if/else clauses, but I have different files and that would not be clean. A attachIfNotNull function (which does not exist) would be helpful in this case, but maybe there are other clean solutions..
public function build()
{
return $this->view('emails.orders.shipped')
->attach('/path/to/file1', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}

You can use File::exists() or Storage::exists():
$view = $this->view('emails.orders.shipped');
return \File::exists('/path/to/file1') ? $view
: $view->attach('/path/to/file1', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);

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

Find data before validate form request laravel

I want to update the data using the request form validation with a unique email role, everything works normally.
Assume I have 3 data from id 1-3 with url:
127.0.0.1:8000/api/user/update/3
Controller:
use App\Http\Requests\Simak\User\Update;
...
public function update(Update $request, $id)
{
try {
// UPDATE DATA
return resp(200, trans('general.message.200'), true);
} catch (\Exception $e) {
// Ambil error
return $e;
}
}
FormRequest "Update":
public function rules()
{
return [
'user_akses_id' => 'required|numeric',
'nama' => 'required|max:50',
'email' => 'required|email|unique:users,email,' . $this->id,
'password' => 'required',
'foto' => 'nullable|image|max:1024|mimes:jpg,png,jpeg',
'ip' => 'nullable|ip',
'status' => 'required|boolean'
];
}
but if the updated id is not found eg:
127.0.0.1:8000/api/user/update/4
The response gets The email has already been taken.
What is the solution so that the return of the data is not found instead of validation first?
The code looks like it should work fine, sharing a few things below that may help.
Solution 1: Check if $this->id contains the id you are updating for.
Solution 2: Try using the following changes, try to get the id from the URL segment.
public function rules()
{
return [
'user_akses_id' => 'required|numeric',
'nama' => 'required|max:50',
'email' => 'required|email|unique:users,email,' . $this->segment(4),
'password' => 'required',
'foto' => 'nullable|image|max:1024|mimes:jpg,png,jpeg',
'ip' => 'nullable|ip',
'status' => 'required|boolean'
];
}
Sharing one more thing that may help you.
Some person uses Request keyword at the end of the request name. The Update sounds generic and the same as the method name you are using the request for. You can use UpdateRequest for more code readability.
What I understand from your question is, you need a way to check if the record really exists or not in the form request. If that's the case create a custom rule that will check if the record exists or not and use that rule inside your request.
CheckRecordRule
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CheckRecordRule implements Rule
{
protected $recordId;
public function __construct($id)
{
$this->recordId = $id;
}
public function passes($attribute, $value)
{
// this will check and return true/false
return User::where('id', $this->recordId)->exists();
}
public function message()
{
return 'Record not found.';
}
}
Update form request
public function rules()
{
return [
'email' => 'required|email|unique:users,email,' . $this->id.'|'. new CheckRecordRule($this->id),
];
}
So when checking for duplicate it will also check if the record really exists or not and then redirect back with the proper message.

Relations in resources?

So I'm planning to start using resources for my "API" (vue endpoint). So I started to search for some tutorials about the subject, and found a youtuber that describes the process. And I started making my own API resource. The youtuber shows briefly how to use the relations, but the thing is that I receive Property [description] does not exist on this collection instance. when trying to use the relation in the resource.
The current setup is:
$stack = Stack::select(['id', 'name', 'subject_id', 'description', 'image'])->where('id', '=', $requestId)->first();
$questions = $stack->load('question.choiceInRandomOrder');
return $questions;
And with resource it would be something like (notice choiceInRandomOrde, I would need that relation also):
return [
'subject' => $this->subject->name,
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->description,
'image' => $this->image,
'questions' => [
'description' => $this->question->description,
'is_info' => $this->question->is_info,
'source' => $this->question->source,
'image' => $this->question->image,
]
];
}
And for testing, I have setup the following in my routes web.php
use App\Stack;
use App\Http\Resources\StackResource;
Route::get('/json', function(){
$stack = Stack::find(2);
return new StackResource($stack);
});
You try to access the name of subject in 'subject' => $this->subject->name, but you do not load the relation.
i don't know if i'm right but i thnk it has to be with the fact that ure not doing an Eloquent call but a Query Builder call (when doing $stack = Stack::select ... ). Why select just some fields in the call if you can choose the parameters to show directly in the model class? (see this).
Try to doing an Eloquent call instead (something like Stack::find(1)) and test it. It should work.

Custom naming in router resources

i have in my controller
public function details($id)
{
$claim = Claim::findOrFail($id);
$details = $claim->details;
return response()->json([], 200);
}
and I have in my routes
Route::resource('claims', 'Admin\\ClaimsController',['names'=> ['details'=>'admin.claims.details'], 'only' => ['index','store','update','destroy','details']]);
when I run php artisan route:list i do not see the admin.claims.details( admin/claims/1/details) in the list
the documentation is pretty vague here so I'm asking how to properly set a custom route? How do I specify if its "POST" or "GET"?
To override the default resource controller actions' route names, you can pass a names array with your options.
For example:
Route::resource('claims', 'ControllerClassName', [
'names' => [
'index' => 'admin.claims.details',
'create' => 'admin.claims.create',
// etc...
],
'only' => [
'index','store','update','destroy','details'
]
]);
REF: https://laravel.com/docs/5.2/controllers#restful-naming-resource-routes
Here are examples of setting custom named get/post routes.
GET Route
Route::get('claims', ['as' => 'admin.claims.details', uses => 'ControllerClassName']);
POST Route
Route::post('claims', ['as' => 'admin.claims.details', uses => 'ControllerClassName']);
REF: https://laravel.com/docs/5.2/routing#named-routes

Passing Object between Models

I have a a couple of Models like so (some code removed for simplicity)
class Poll extends Model
{
public function poll()
{
return $this->hasOne('App\PollQuestion', 'pollId');
}
}
class PollQuestion extends Model
{
public function poll()
{
return $this->belongsTo('App\Poll');
}
}
I then have all my routes set up, for the PollQuestion they currently look like this
Route::model('polls.questions', 'PollQuestion');
Route::get('/admin/polls/{id}/addquestions', [
'as' => 'polls.questions.create',
'uses' => 'PollQuestionController#addQuestions'
]);
Route::post('/admin/polls/{id}/storequestions', [
'as' => 'polls.questions.store',
'uses' => 'PollQuestionController#storeQuestion'
]);
In my PollQuestionController, to see the questions view I have
public function addQuestions(Request $request)
{
$poll = Poll::where('id', '=', $request->id)->first();
return view('poll.admin.questions', compact('poll'));
}
Inside this view if I dump the poll e.g.
{{ dd($poll) }}
I can see what I expect to see. For the question form, I am doing
{!! Form::model(new App\PollQuestion, [
'route' => ['polls.questions.store', $poll]
]) !!}
So I presume that should pass my store function the Poll Object I previously dumped. However, in the store function, I do
public function storeQuestion(Request $request, Poll $poll) {
dd($poll);
}
And it shows Null. Why would this happen seeing that I am passing it the Poll Object I had previously dumped?
Thanks
You need use Route Model Binding correctly. According to Laravel Documentation
Route::model('poll-question', App\PollQuestion::class);
Route::get('/admin/polls/{poll-question}/addquestions', [
'as' => 'polls.questions.create',
'uses' => 'PollQuestionController#addQuestions'
]);
Route::post('/admin/polls/{poll-question}/storequestions', [
'as' => 'polls.questions.store',
'uses' => 'PollQuestionController#storeQuestion'
]);
Route::model define a variable in the route like a Model. This method searches the primary key passed in the route and retrieve the Model.

Resources