Prevent a Laravel notification to be sent - php

I'm working on a feature to send a "cascade" or a chain of notifications with a delay between them with different channel. For example:
Email -> (30 minutes later) -> Push -> (30 minutes later) -> SMS
That flow is working good, now is the user completes something or does an action I want the chain to stop. So I stop or prevent the notification to be sent. This is what I've tried, but nothing seems to stop them.
I've tried:
public function via($notifiable)
{
if (whatever condition to stop) {
return null; // also tried with return []
}
return ['mail'];
}
Also
public function __construct(array $data = [])
{
if (whatever condition to stop) {
exit; // are you desperate, bruh?
}
}
Is there something super obvious I'm not seeing? Might be related to our custom scheduler, tho.
Do you have an idea where can I break the app to prevent the notification to be sent?

Actually, this was enough:
public function via($notifiable)
{
if (whatever condition to stop) {
return [];
}
return ['mail'];
}
The problem was something else, Docker was showing me a cached version of the files, so they were always returning a return ['mail'];

Related

two returns in one function for laravel api

Is it true that in laravel api the first return will break the function and won't let it continue executing the rest of the function?
I have this function
public function get(Request $request)
{
$users = User::doesntHave('reservations')->get();
if ($users) {
return Response::json($users);
}
// do something here if no users attached with reservation and do
// return Response::json($users);
}
when I do this I get no data! What is my mistake ?
I guess what you want is to check if result is empty or not:
if (!$users->isEmpty()) {
return Response::json($users);
}
return Response::json('There is no result');
And yes, you can return response only once.
This is not exclusive to Laravel, but yes. A return statement per definition will return to the caller of the function immediately, so nothing else after it will be executed.
If called from within a function, the return statement immediately ends execution of the current function, and returns its argument as the value of the function call. return also ends the execution of an eval() statement or script file.

How can i check if the Gate::before method was used ?

I used laravel's authorization in my code and defined the before method in my service provider as shown in the documentation:
public function before($user, $ability)
{
if ($user->email == 'super#mail.com') {
return true;
}
}
Sometimes I need to know if the checks were intercepted. So, instead of checking the mail (or any other condition for superuser) in different places, I want to check if this intercepting method is used. I mean instead of using this (if email is equal...) condition in different places again and again. I want to set this super admin condition only once (in the service provider) and then check it when needed. Something like:
\Gate::isIntercepted
Instead of simply returning true, you can return an authorization response:
public function before($user, $ability)
{
if ($user->email == 'super#mail.com') {
return $this->allow('superadmin');
}
}
You'll get the response later on if you use authorize:
public function update(Post $post)
{
$response = $this->authorize($post);
if ($reponse->message() == 'superadmin') {
// do something
}
}

Laravel - return a redirectResponse selectively generated in a function

Part of my application is a multi-stage checkout process; during the latter pages of this I first run a sanity check on each request to verify the user actually has some items in their basket: if not they're sent back to the beginning.
I have a controller function like this which is called from multiple routes for DRY purposes.
private function checkBasketFull($request)
{
if (self::isBasketEmpty($request)) {
return redirect('/')->with('status', config('app.empty_basket_message'));
}
}
When I call it, I can't just do:
self::checkBasketFull($request);
because without a return the redirect doesn't fire, only the session data is sent.
And I can't do:
return self::checkBasketFull($request);
because that will give an error if there's no redirect or abort the method if checkBasketFull returns anything else.
My current (working) code is:
$check = self::checkBasketFull($request);
if ($check) {
return $check;
}
Is there an alternative way of writing this on a single line, or modifying the checkBasketFull function, so the redirect will occur if the basket is empty but execution will continue as normal if it isn't?
Either use this:
if ($redirect = self::checkBasketFull($request)) return $redirect;
Or throw an error and catch it in the global error handler.
However, instead of returning and checking that for a redirect like that, I'd much rather keep it as two completely separate methods:
public function someRoute(Request $request)
{
if ($this->isBasketEmpty($request)) return $this->redirectBasketEmpty();
// Continue processing this request...
}
protected function isBasketEmpty(request)
{
// run your login here...
}
protected function redirectBasketEmpty()
{
return redirect('/')->with('status', config('app.empty_basket_message'));
}
Feels cleaner to me.

Why cookie isn't set in Laravel Lumen

This question is the following of this question.
I have a message in my view who says : This site uses cookie [...] Close.
When user click on Close, an ajax request is send to the controller. The function is the following :
public function acceptCookie(Request $request)
{
if ($request->valid == 'accept') {
$response = new Response('acceptCookie');
if ($response->withCookie(cookie('acceptCookie', 'accepte', 44000))) {
return Response()->json(array('statut' => 'Succes'));
} else {
return Response()->json(array('statut' => 'Erreur'));
}
} else {
return Response()->json(array('statut' => 'Erreur'));
}
}
I haven't any error and JSON returns always {"statut":"Succes"}
Why the cookie isn't set ?
Based on the Lumen documentation, it appears as though you need to queue the cookie for a response such as the one in your example. Here's what the docs say:
Queueing A Cookie For The Next Response
If you would like to set a cookie before a response has been created,
use the Cookie::queue() method. The cookie will automatically be
attached to the final response from your application.
Cookie::queue($name, $value, $minutes);
My suggestion would be to try replacing the withCookie with queuing the cookie instead. But, you might need to rewrite the function a bit in order to accomodate because it appears as though you're trying to send to responses from one request.
Hope this works for you!
Based on Illuminate\Http\ResponseTrait line 28, the Illuminate\Http\Response::withCookie method returning $this.
/**
* Add a cookie to the response.
*
* #param \Symfony\Component\HttpFoundation\Cookie $cookie
* #return $this
*/
public function withCookie(Cookie $cookie)
{
$this->headers->setCookie($cookie);
return $this;
}
Means you have logic failure in your code.
// This always return Illuminate\Http\Response instance,
// thus it will never reach ELSE statement forever.
if ($response->withCookie(cookie('acceptCookie', 'accepte', 44000))) {
return Response()->json(array('statut' => 'Succes'));
} else {
return Response()->json(array('statut' => 'Erreur'));
}

Laravel redirect from private method with errors

I have the following code:
public function store(Request $request)
{
$this->validateData($request->all());
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
private function validateData($requestParams)
{
try
{
$validator->validate( $requestParams );
}
catch ( ValidationException $e )
{
redirect()->action('controller#create')->withInput()->withErrors( $e->get_errors() )->send();
exit(); // this causes the withErrors to not be there
}
}
If I remove the exit();, the error messages will appear, but also the store function will be executed (see // store something). I know I can rewrite my code like:
if($this->validateData($request->all()))
{
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
But I don't want the ugly if statement here. There must be a way to redirect with the flash messages without it.
tl;dr
Update your private method code like this to make the redirection work with $errors variable visible:
private function validateData($requestParams)
{
try
{
$validator->validate( $requestParams );
}
catch ( ValidationException $e )
{
$resp = redirect()->action('WelcomeController#index')->withInput()->withErrors($e->get_errors());
\Session::driver()->save();
$resp->send();
exit();
}
}
explaination
When exiting in the middle of your controller, there are some job which is executed in the application termination will not be execute anymore. In your case, the Session middleware terminate method will not be called. Let see its content (ref):
public function terminate($request, $response)
{
if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions())
{
$this->manager->driver()->save();
}
}
Now, look at the save method of our Session driver (ref)
public function save()
{
$this->addBagDataToSession();
$this->ageFlashData();
$this->handler->write($this->getId(), $this->prepareForStorage(serialize($this->attributes)));
$this->started = false;
}
As you can see, your flash data is only be saved when the Session middleware terminates successfully. With your old code, the flash data will be lost!
What I do with my code is calling the save method manually before sending our response to the browser. However, I still recommend you bring the redirection to the public controller method.
Well I don't see any problem using the if statement there. Basically you do not stop the code execution, so thats why the store function is executed, even if your validation fails. The redirect function just sends a header with the redirect location, it does not abort the code after it to be executed. It works with exit(), because it sends the redirect headers and stop the rest of the code to be exeuted.
This is not ugly, it is clean and clear and I suggest you to use this. This is a good example of right if statement usage - if one conditions i met then do this. In your case if your validation passes, just store the object. (Just remember to modify your validate function to return true or false)
if($this->validateData($request->all()))
{
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
The other possible solution is to use try .. catch block like this
public function store(Request $request)
{
try {
$this->validateData($request->all());
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
} catch ( ValidationException $e ) {
return redirect()->action('controller#create')->withInput()->withErrors( $e->get_errors() );
}
}
private function validateData($requestParams)
{
// Your validation logic here
$validator->validate( $requestParams );
}
you just forgot to 'return' after the validation exception :D, then you would not have to 'exit;'

Categories