Laravel 8: how to send email with attachment using markdown - laravel

I would like to know how to include an attachment when sending an email using laravel and markdown.
This is the class InvoiceEmail extends Mailable
protected $data;
public function __construct($data)
{
$this->data = $data;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('tes#test.com')->subject('Order')->markdown('emails.invoiceEmail')->with('data',$this->data);
}
In the controller called OrderController I send the email:
$customerPDF = 'file.pdf';
$data = array(
'name' => $request->vendor_name,
'company' => $request->company,
'vat'=> $request->vat,
'category' => $request->category,
'url' => Route('vendor.reg.enable.account',$enableCode)
);
Mail::to($request->email)->send(new InvoiceEmail($data));
My question is: how can I attach the customerPDF?

Try to add attach method call to InvoiceEmail#build
public function build()
{
return $this->from('tes#test.com')
->subject('Order')
->markdown('emails.invoiceEmail')
->with('data',$this->data)
->attach(asset($this->data->pdf_file), ['mime' => 'application/pdf']);
}

Related

Laravel Policy Always returns 403 unauthorized

I'm making an app that uses jwt as authentication system ,
when I try to update my Category model the policy always returns 403 unauthorized,
I'm using apiResource to crud my model.
my code
in api.php:
Route::apiResource('category', CategoryController::class);
in CategoryController.php:
public function update(Request $request, $id)
{
// print_r($request->all());
$validator = Validator::make(
$request->all(),
[
'name' => 'required|min:2|unique:categories,name,' . $request->id,
'description' => 'required|min:1',
],
[
"name.unique" => "اسم الصنف مستخدم مسبقا",
"name.required" => "اسم الصنف مطلوب",
"name.min" => "اسم الصنف يجب أن يحتوي على حرفين على الأقل",
"description.required" => "وصف الصنف مطلوب",
]
);
if ($validator->fails()) {
return response()->json(['errors' => $validator->messages(), 'status' => 422], 200);
}
$category = Category::find($id);
$category->name = $request->name;
$category->description = $request->description;
$category->save();
return response()->json([
"message" => "تم تحديث الصنف",
"status" => 200
], 200);
}
in CategoryPolicy.php:
public function update(User $user, Category $category)
{
return $category->user_id === $user->id;
}
It seems like the request is not even reaching the update method in CategoryPolicy.php
because even if the method always returning true it's not working :
public function update(User $user, Category $category)
{
return true;
}
any way the viewAny method is working as expected.
I'm using axios to fetch and update data and I'm sending the request with the bearer token and every thing is working ok except the issue above.
In CategoryController.php, instead of injecting $id:
public function update(Request $request, $id)
Try injecting the type-hinted model instance:
public function update(Request $request, Category $category)
And remove the find() command:
//$category = Category::find($id);
When generating new controllers, you can also use this artisan command to include type-hinted models in the function arguments.
php artisan make:controller CategoryController --api --model=Category
It's hard to see what is going wrong because it can also be the middleware and JWT token. What you could do is in your update method check if the user is logged in, add the following as the first line in the method. If false please check your JWT implementation
dd(auth()->check());
I would also suggest clean up your controller:
class CategoryController
{
/**
* CateogryController constructor.
*/
public function __construct()
{
$this->authorizeResource(Category::class); // if your are using CRUD, validate like this
}
/**
* Update specific resource.
*
* #param Category $category
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function update(Category $category, CategoryRequest $request): JsonResponse
{
// notice the model route binding.
$this->authorize('update', $category); // If you only have update method, but remove the __construct.
$category->update([
'name' => $request->get('name'),
'description' => $request->get('description')
]);
return response()->json(['message' => 'تم تحديث الصنف']); // take the 200 from the headers, not add it in as text.
}
}
Your request looks similar to this:
class CategoryRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true; // you could consider to validate the user->category relation. I like it more separated and put it in a separated policy.
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|min:2|unique:categories,name',
'description' => 'required|min:1',
];
}
/**
* #return string[]
*/
public function messages()
{
return [
"name.unique" => "اسم الصنف مستخدم مسبقا",
"name.required" => "اسم الصنف مطلوب",
"name.min" => "اسم الصنف يجب أن يحتوي على حرفين على الأقل",
"description.required" => "وصف الصنف مطلوب",
];
}
}
And your policy like:
class CategoryPolicy
{
use HandlesAuthorization;
/**
* Determine if the user can update category resource.
*
* #param User $user
* #param Category $category
* #return bool
*/
public function update(User $user, Category $category): bool
{
return $user->categories()->where('id', $category->id)->exists(); // or somthing like this.
}
}

laravel api not showing message from request validation

im having problems with error handler in my backend laravel api, don't show error message from validation.
routes/api
<?php
Route::group([
'middleware' => 'api',
], function ($router) {
Route::post('access/sign-up', 'AuthenticationController#signUp');
});
AuthenticationController
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\SignUpRequest;
use Illuminate\Http\Request;
use App\User;
class AuthenticationController extends Controller
{
/**
* Create a new AuthenticationController instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['signUp']]);
}
/**
*
*
* #return \Illuminate\Http\JsonResponse
*/
public function signUp(SignUpRequest $request)
{
//User::create($request->all());
return response()->json([
'data' => 'Usuario creado correctamente.'
]);
}
}
SignUpRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SignUpRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required|email|unique:users',
'firstName' => 'required',
'password' => 'required',
];
}
}
The thing is that when by postman i send password in blank or not send mail for example it send me to main page insted of sending me a json with all the errors like before. I don't know what to do.
I tryed with custom message in SignUpRequest, but that's not the problem, think some kind of redirect in error handler.
You need to check just one setting must add Accept Type in header data in Postman
Accept : application/json
that is work in my case...
Just add that code to SignUpRequest
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json($validator->errors(), 422));
}
Using the Form request; If validation fails, a redirect response will be generated to send the user back to their previous location. That's why you are redirecting back to the page instead of the response JSON.
Laravel have one protected method "failedValidation" method. Overwrite this method in your form request class.
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
throw new \Illuminate\Validation\ValidationException(response()->json($validator->errors(), 422));
}
Update for laravel 8, the first parameter in ValidationException is the Validator class:
protected function failedValidation(Validator $validator)
{
throw new ValidationException($validator, response()->json($validator->errors(), 422));
}
TL;DR Add the following header: Accept: application/json
As mentioned by a few users over here, by default, when a Form Request fails, the application will attempt to redirect the user back to their previous location. That's why you're receiving a webpage instead of a JSON response.
We can also override the "failedValidation" method. But I would recommend setting the following header on the request Accept: application/json.
Laravel will return the appropriate response based on Accept header. For application/json Laravel will return a JSON response with form errors.
Simply, in your App\Exceptions\Handler, you have to register your custom exception handling in register() method:
public function register() {
$this->renderable( function ( ValidationException $ex, $request ) {
$response = [
'ErrorCode' => 'my_error_code',
'ErrorMessage' => $ex->validator->errors()
];
return response()->json( $response );
} );
}
add this method to your formRequest class. It works in laravel 9
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
class PostRequest extends FormRequest
{
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'success' => false,
'message' => 'Validation errors',
'data' => $validator->errors(),
]));
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
switch ($this->method()) {
case 'GET':
case 'DELETE':{
return [];
}
case 'POST':{
return [
'title' => 'string|unique:posts|required',
'body' => 'required',
'image' => 'string|nullable',
];
}
case 'PUT':{
return [
'title' => 'string|unique:posts|required',
'body' => 'required',
'image' => 'string|nullable',
];
}
}
}
}
Just add that code to App\Exceptions\Handler:
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return response()->json([
'status' => false,
'error' => [
'type' => 'form',
'code' => 400,
'description' => 'Bad request.',
'detail' => $exception->validator->errors()
]
], 422);
}
return parent::render($request, $exception);
}

How to send Data from Laravel Controller to Mailable Class

I have created Mailable Class in Laravel 5.3 which calls the view. However, I need to pass some variables from my Controller to the Mailable Class and then use these values inside the View. This is my set-up:
Controller:
$mailData = array(
'userId' => $result['user_id'],
'action' => $result['user_action'],
'object' => $result['user_object'],
);
Mail::send(new Notification($mailData));
Mailable:
class Notification extends Mailable
{
use Queueable, SerializesModels;
protected $mailData;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($mailData)
{
$this->$mailData = $mailData;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
// Array for Blade
$input = array(
'action' => $mailData['action'],
'object' => $mailData['object'],
);
return $this->view('emails.notification')
->with([
'inputs' => $this->input,
]);
}
}
The above gives me the error:
ErrorException in Notification.php line 25:
Array to string conversion
Referring to the construct line in Mailable Class:
$this->$mailData = $mailData;
What have I got wrong here? How do I correctly pass array values from Controller to Mailable and then use with to pass them on to the View?
Try this:
public $mailData;
public function __construct($mailData)
{
$this->mailData = $mailData;
}
public function build()
{
// Array for Blade
$input = array(
'action' => $this->mailData['action'],
'object' => $this->mailData['object'],
);
return $this->view('emails.notification')
->with([
'inputs' => $input,
]);
}
Docs

How do I implement a reusable validator that I have created for cakephp 3?

I have created a validation rule that checks to see if a url actually exists. I can make it work fine if I implement it as a custom rule within my tables validators. However I would like to make it reusable... I have tried a few different ways and I either get told that the method does not exist or cannot be found, or that I am calling to a member function on NULL
My current error is:
Error: Call to a member function add() on null
I am fairly new to MVC programming and very new to Cakephp
As per the documentation (and my understanding of it) here is my new validator class:
<?php
// In src/Model/Validation/ContactValidator.php
namespace App\Model\Validation;
use Cake\Validation\Validator;
class ContactValidator extends Validator
{
public function __construct()
{
parent::__construct();
$validator
->add('validDomain','custom',[
'rule' => function($value){
$url = parse_url($value);
$host = $url['host'];
if($host != gethostbyname($host)){
return true;
}
return false;
}
]);
}
}
?>
here is my Table (I have deleted all of the validator rules but the one I am trying to get to work for this example):
<?php
namespace App\Model\Table;
use App\Model\Entity\Agent;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Event\Event, ArrayObject;
/**
* Agents Model
*
* #property \Cake\ORM\Association\BelongsTo $Users
* #property \Cake\ORM\Association\BelongsTo $Agencies
* #property \Cake\ORM\Association\HasMany $Pictures
* #property \Cake\ORM\Association\HasMany $Properties
* #property \Cake\ORM\Association\HasMany $SoldProperties
* #property \Cake\ORM\Association\BelongsToMany $Regions
*/
class AgentsTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->table('agents');
$this->displayField('user_id');
$this->primaryKey('user_id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Agencies', [
'foreignKey' => 'agency_id'
]);
$this->hasMany('Pictures', [
'foreignKey' => 'agent_id'
]);
$this->hasMany('Properties', [
'foreignKey' => 'agent_id'
]);
$this->hasMany('SoldProperties', [
'foreignKey' => 'agent_id'
]);
$this->belongsToMany('Regions', [
'foreignKey' => 'agent_id',
'targetForeignKey' => 'region_id',
'joinTable' => 'agents_regions'
]);
}
/**
* Default validation rules.
*
* #param \Cake\Validation\Validator $validator Validator instance.
* #return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator)
{
$validator = new \App\Model\Validation\ContactValidator;
$validator
->add('agency_domain', 'valid',['rule' => 'validDomain', 'provider'=>'ContactValidator', 'message' => 'The url you have supplied does not exist!']);
return $validator;
}
public function isOwnedBy($userId)
{
return $this->exists(['user_id' => $userId]);
}
/**
* Returns a rules checker object that will be used for validating
* application integrity.
*
* #param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* #return \Cake\ORM\RulesChecker
*/
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
{
if(isset($data['agency_domain']))
{
$data['agency_domain']=strtolower($data['agency_domain']);
if(strpos($data['agency_domain'],"http")===false){
$data['agency_domain'] = "http://".$data['agency_domain'];
}
}
}
}
if someone could point me in the right direction or even show me a working example of how to do this it would be greatly appreciated.
Just create an object of Validator class.
public function __construct()
{
parent::__construct();
$validator = new Validator();
$validator
->add('validDomain','custom',[
'rule' => function($value){
$url = parse_url($value);
$host = $url['host'];
if($host != gethostbyname($host)){
return true;
}
return false;
}
]);
}

Validation fileds from ajax request [Laravel 5]

Here is my validation request :rules
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Support\Facades\Auth;
class UpdateCommentRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize() {
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules() {
$user = Auth::user()->id;
return [
'comment' => 'required|between:15,600',
'projectID' => "required|exists:project_group,project_id,user_id,$user|numeric",
'order' => "required|numeric",
'level' => "required|numeric"
];
}
}
And in my model I have like this:
public function apiUpdateComment(UpdateCommentRequest $request){
$comment = Comment::find(Input::get("order"));
$comment->text = Input::get('comment');
if($comment->save()){
return 'success';
}
}
This fileds I need to validate agins rules array:
array(
'comment' => Input::get('comment'),
'projectID' => Input::get('projectID'),
'order' => Input::get("order"),
'level' => Input::get("level"),
);
I need to check if all rules are ok and then update comment... Anyone can help?
public function apiUpdateComment(UpdateCommentRequest $request){
$comment = Comment::find($request->get("order"));
$comment->text = $request->get('comment');
if($comment->save()){
return 'success';
}
}
The logic behind the code:
A post request is send the the server and the route file sends it the the apiUpdateComment with all variables inside the $request. But before the code of the function is executed the validator checks the rules in your UpdateCommentRequest. If the test fails it will return errors. If it pass a comment with the id will be updated.

Resources