laravel public variable becomes null within model - php

I am trying to set webhook url for each event I define.
So I have something like below.
UserModel
<?php
namespace app\Model\User;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Model\Device as DeviceModel;
/**
* Class User
* #package app\Model\User
*/
class User extends Authenticatable
{
use Notifiable;
/**
* #var string
*/
public $slackChannel;
/**
* Route notifications for the Nexmo channel.
*
* #return string
*/
public function routeNotificationForSlack() : string
{
if ( $this->slackChannel ){
return config('slack.channels.'.$this->slackChannel);
}
}
/**
* #param string $slackChannel
* #return object
*/
public function slackChannel(string $slackChannel) : object
{
$this->slackChannel = $slackChannel;
return $this;
}
}
Job
public function handle()
{
foreach( $this->users as $user ) {
$recipient = $user;
$user->slackChannel('order-requested')->notify(new OrderSubmittedByClient($this->order, $recipient));
}
}
config/slack
return [
'channels' => [
'order-requested' => 'https://hooks.slack.com/services/xxxxxx',
]
];
Even after I set $this->slackChannel to certain string, when it comes to routeNotificationForSlack, it returns null.
What am I doing wrong? I suspect jwt in the middleware, but will that matter?

Related

Laravel 9 Accessor not Returning Value

I have a Reminder model that has a sound_path column. I created an accessor for that column in the model but it's returning null and I doubled checked that the database has a value in that column. What am I doing wrong?
Note: Of course I can call $this->sound_path directly in soundPathUrl mutator without creating the accessor from the first place but I'm interested to know why if I called the accessor is not returning any value.
Reminder model
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Spatie\Translatable\HasTranslations;
class Reminder extends BaseModel
{
use HasFactory, SoftDeletes, HasTranslations;
public $translatable = [
'title__ml',
];
protected $casts = [
'available_days_for_reminder' => 'json',
'is_multiple_days_allowed' => 'boolean'
];
protected $appends = ['sound_path_url'];
/**
* Get the sound path
*
* #param string $value
* #return \Illuminate\Database\Eloquent\Casts\Attribute
*/
protected function soundPath(): Attribute
{
return Attribute::make(
get: fn ($value) => $value
);
}
/**
* Get sound path download URL
*
* #return \Illuminate\Database\Eloquent\Casts\Attribute
*/
protected function soundPathUrl(): Attribute
{
return new Attribute(
get: fn () => asset('storage/' . $this->soundPath),
);
}
}
Reminder controller
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\Reminder\ReminderCollection;
use App\Http\Resources\Reminder\ReminderResource;
use App\Models\Reminder;
use Exception;
use Illuminate\Http\Request;
class ReminderController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$reminders = Reminder::paginate(5);
return ReminderCollection::collection($reminders);
}
}
ReminderCollection API resource
namespace App\Http\Resources\Reminder;
use Illuminate\Http\Resources\Json\JsonResource;
class ReminderCollection extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title__ml,
'sound' => $this->sound_path_url
];
}
}
Screenshot of Response
At the arrow there should be the value of sound_path.
Your issue is that you are calling $this->soundPath instead of $this->sound_path in soundPathUrl method...
So, you should have this:
/**
* Get sound path download URL
*
* #return \Illuminate\Database\Eloquent\Casts\Attribute
*/
protected function soundPathUrl(): Attribute
{
return new Attribute(
get: fn () => asset('storage/' . $this->sound_path),
);
}
Check the documentation and you will see you still have to call your properties using snake_case.
It seems soundPath accessor does nothing, it returns the same value which seems empty. Where does soundPath value come from? First, make sure, soundPath itself has any value within that function.
Call soundPath as a function
protected function soundPathUrl(): Attribute
{
return new Attribute(
get: fn () => asset('storage/' . $this->soundPath()),
);
}
}

Add bcc to mail when user verification mail or password reset mail trigger in laravel default Auth

I am trying to add bbc to verification and password rest link mail in laravel default auth system. There is no mail function in laravel default Auth system how can I add ->bcc() in verification mail and password reset mail.
Any help would be appriciated.
like this
Mail::to($request->user())
->cc($moreUsers)
->bcc('admin#example.com')
->send(new OrderShipped($order));
forgotpasswordcontroller.php
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Password;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Auth;
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
public function __construct()
{
$this->middleware('guest');
}
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
$response = $this->broker()->sendResetLink(
$request->only('email')
);
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($request, $response)
: $this->sendResetLinkFailedResponse($request, $response);
}
}
verificationcontroller.php
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
use VerifiesEmails;
protected $redirectTo = 'shop/home';
public function __construct()
{
$this->middleware('auth')->only('verify');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}
php artisan make:notification ResetPassword
then add this code at ResetPassword
<?php
namespace App\Notifications;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;
class ResetPassword extends Notification
{
/**
* The password reset token.
*
* #var string
*/
public $token;
/**
* The callback that should be used to build the mail message.
*
* #var \Closure|null
*/
public static $toMailCallback;
/**
* Create a notification instance.
*
* #param string $token
* #return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's channels.
*
* #param mixed $notifiable
* #return array|string
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
return (new MailMessage)
->subject(Lang::get('Reset Password Notification'))
->bcc('info#example.com') //add bcc for another email
->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::get('Reset Password'), url(route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
->line(Lang::get('If you did not request a password reset, no further action is required.'));
}
/**
* Set a callback that should be used when building the notification mail message.
*
* #param \Closure $callback
* #return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}
then override method at user class
<?php
namespace App;
use App\Helper\Helper;
use App\Notifications\ResetPassword;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $table = 'users';
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
// Override sendPasswordResetNotification
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPassword($token));
}
}
and do same thing to Verification
Override method for EmailVerification
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}
You can also listen for the mail sent event, like so:
app/Providers/EventServiceProvider.php
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
MessageSending::class => [
LogOutboundMessages::class
]
];
Then, app/Listeners/LogOutboundMessages.php
class LogOutboundMessages
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param object $event
* #return void
*/
public function handle(MessageSending $event)
{
$event->message->addBcc('admin#example.com');
}
}

Laravel policy return unauthorized

I am trying to implement policies in my project. All tries have proven unsuccessful despite following documentation to the letter. And also read numerous posts on SO about it and other media. I did as described in docs, but nonetheless it doesn't work. What gives?
In AuthServiceProvider:
<?php
namespace App\Providers;
use App\User;
use App\Job;
use App\Policies\JobPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Job' => 'App\Policies\JobPolicy',
//Job::class => JobPolicy::class,
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
In policy:
<?php
namespace App\Policies;
use App\Job;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class JobPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any jobs.
*
* #param \App\User $user
* #return mixed
*/
public function viewAny(User $user,Job $job)
{
//return (($user->isAdmin() || $user->isModerator() || $user->isUser()) && $user->status==1);
//return ($user->isMod());
return true;
}
In controller:
public function index()
{
$this->authorize('viewAny', User::class, Job::class);
return view("jobs.index");
}
My User model:
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Role;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',"role_id"
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function role(){
return $this->belongsTo("App\Role", "role_id");
}
public function isMod()
{
$user = User::find(auth()->user()->id);
$role = $user->role()->first()->name;
if($role==="job board moderator"){
return true;
}
else{
return false;
}
}
}
And Job model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\User;
class Job extends Model
{
protected $fillable = [
"title", "description", "email"
];
public function user(){
return $this->belongsTo("App\User","user_id");
}
}
In policy:
public function viewAny(User $user)
{
return true;
}
In controller:
public function index()
{
$this->authorize('viewAny', Job::class);
return view("jobs.index");
}
The way to call a model policy method changes depending on the number of parameters it has.
No object
/* In policy*/
public function viewAny(User $user)
/* In controller */
$this->authorize('viewAny', Job::class)`
1 object
/* In policy*/
public function view(User $user, Job $job)
/* In controller */
$this->authorize('view', $job)
More than 1 object
/* In policy*/
public function view(User $user, Job $job, AnotherModel $model)
/* In controller */
$this->authorize('view', [$job, $model])
Source: https://laravel.com/docs/5.8/authorization#creating-policies

Laravel Method notify does not exist

I am trying to notify user if a new form is inserted to database, but I get this error:
BadMethodCallException in Macroable.php line 74: Method notify does not exist.
This is the notification class
<?php
namespace App\Notifications\Admin;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use App\Models\Admin\Forms\Prescriptions;
class PrescriptionNotification extends Notification
{
use Queueable;
public $Prescription;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Prescriptions $Prescription)
{
$this->Prescriptions = $Prescription;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = url('/admin/prescriptions/edit/'.$this->Prescriptions->id);
return (new MailMessage)
->line('New form')
->action('View', $url)
->line('');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'test' => 'test'
];
}
}
And in my controller I am doing this:
$users = App\User::all()->where('role', 3);
//trigger email notification
$Prescription = Prescriptions::first();
$users->notify(new PrescriptionNotification($Prescription));
Been following This tutorial, but still to no avail. I have Notifiable in the user model. What else can be done? I am losing my mind what causes this error.
As requested my User class:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
//is admin
public function isAdmin()
{
return $this->role; // this looks for an admin column in your users table
}
//relationship with prescription forms
public function confirmations()
{
return $this->hasMany('App\Models\Admin\Forms\Prescription_confirmations', 'physician_id');
}
//relationship with prescriptions forms
public function prescriptions()
{
return $this->hasMany('App\Models\Admin\Forms\Prescriptions', 'physician_id');
}
}
$users is a collection, so you are calling notify method on a collection which will lead to error. And the method notify only exist on user object instance.
You can do this
<?php
foreach ($users as $user) {
$user->notify(new PrescriptionNotification($Prescription));
}
Think it's easier to use Notification Facade.
Notification::send($users, new PrescriptionNotification($Prescription));
And you need to call Notification facade with use like this,
use Illuminate\Support\Facades\Notification;
Also, you can use higher-order messages:
$users->each->notify(new PrescriptionNotification($Prescription));
In my case I forgot to implement the Notifiable trait.
So you have a users class for example:
use Illuminate\Notifications\Notifiable;
class Users {
use Notifiable;
...
}

User model error after Laravel update (Class User contains 3 abstract method)

After I update my laravel using composer update, I got this
"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException",
"message":"Class User contains 3 abstract methods and must therefore be declared abstract or implement the remaining methods (Illuminate\\Auth\\UserInterface::setRememberToken, Illuminate\\Auth\\UserInterface::getRememberTokenName, Illuminate\\Auth\\Reminders\\RemindableInterface::getReminderEmail)",
"file":"D:\app\\models\\User.php",
"line":54
error when authenticating.
This error happened because of the latest commit.
You can check the upgrade documentation here, to fix this issue.
As stated, add the following to your User.php model class:
public function getRememberToken()
{
return $this->remember_token;
}
public function setRememberToken($value)
{
$this->remember_token = $value;
}
public function getRememberTokenName()
{
return 'remember_token';
}
This is what worked for me by adding the below to app/User
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
Example app/User
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* #return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
}

Categories