I have a strange problem with Laravel's schedule that I am trying to solve:
ERROR: exception 'InvalidArgumentException' with message 'Invalid scheduled callback event. Must be string or callable'.
This is the line of Kernel.php:
$schedule->call(\App\Classes\Maintenance::deleteAllRecord())->daily();
And this is the function :
public static function deleteAllRecord()
{
$data=\App\LastSeen::all();
foreach ($data as $dt)
{
$dt->delete();
}
return 'OK';
}
I try also return true , but I had the same problem.I'm sure that the problem is the type of return. Where I make the mistake ?
I hope I was exhaustive. I wait answer ^_^ have a good day.
You can call \App\Classes\Maintenance::deleteAllRecord() inside the callback. Like
$schedule->call(function(){
\App\Classes\Maintenance::deleteAllRecord();
})->daily();
Related
As we all painfully know, this message is generated when an error occurs before Laravel has had a chance to instantiate a "Log" class instance to handle it. And... it therefore seems to completely conceal just what the underlying error is!
In my case the php artisan command won't run either.
Is there any way to find out what's wrong without "blind guessing?"
When you want to handle exceptions, a good way to catch it would be to implement a way to trapping exception out of controllers or services. You can do it in the "render" method of the App\Exceptions\Handler class. In this "render" method, you can write a block of "if" code to show the messages generated by an exception when Laravel throws an exception. For example:
public function render($request, Exception $exception)
{
if($exception) {
// do something
return response()->json(['error' => $exception->getMessage(),
$exception->getTraceAsString()], 500);
}
// Or if you created an exception specialization
if ($exception instanceof MyCustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
This was indeed caused by a syntax error, and I'm really surprised that Laravel had even managed to "get started" by that point in time. I literally found it by looking with git at a list of files that had recently changed.
Edited:
I have a custom exception with render method which is being called when I throw it e.g. from controller, but not being called when I throw it in View composer.
So when I do something like that
public function compose(View $view)
{
throw new CustomException();
}
and put dd() to exception render method
public function render()
{
dd('render is called');
}
I get no result.
If I log my exception directly, finds out that first the CustomException being thrown, then as the result I see ErrorException.
I found a place where it being thrown.
\Illuminate\View\Engines\CompilerEngine::handleViewException
protected function handleViewException(Exception $e, $obLevel)
{
$e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e);
parent::handleViewException($e, $obLevel);
}
I didn't found any mentions in Laravel docs about that case.
I found a tread on github with the same issue: https://github.com/laravel/framework/issues/24658
So the question is, is this expected? Is there any adequate way to avoid this behaviour?
Edit
So, as you know, any exception during view compilation is intercepted and rethrown as ErrorException or as FatalThrowableError.
What you can do is intercept ErrorException and check if ($e->getPrevious() instanceof \CustomException) if so, you do your code, else, let the handler continue.
So I've found working solution for myself.
I've extended CompilerEngine and added additional processing in order to not throw ErrorException when I don't want to.
The important thing is - your resulting Exception must be inherited from ErrorException. Otherwise you will face multiple calls to \App\View\Engines\CompilerEngine::handleViewException which can break your logic and write multiple log entities to your log file.
I'm trying to find a clean way to override the AuthorizationException to take a dynamic string that can be passed back when a Policy fails.
Things I know I can do are:
Wrap the Policy in the Controller with a try-catch, then rethrow a custom exception that takes a specific string, which seems a bit verbose
abort(403, '...') in the Policy prior to returning, which seems a bit hacky since policies are already doing the work
and then in /Exceptions/Handler::render I can send back the response as JSON
Is there a nicer way to do this to get a message in the response of a policy failure? Or is 1 or 2 my best choices.
I noticed if you throw AuthorizationException($message) in a policy using Laravel's exception it jumps you out of the policy, but continues execution in the controller, and doesn't progress to Handler::render. Which I'm assuming this is them handling the exception somehow, but I couldn't find where they were doing it... so if anyone finds where this is happening I'd still like to know.
If you create your own AuthorizationException and throw it, it will stop execution as expected, and drop into Handler::render so I ended up adding this method to my policy:
use App\Exceptions\AuthorizationException;
// ... removed for brevity
private function throwExceptionIfNotPermitted(bool $hasPermission = false, bool $allowExceptions = false, $exceptionMessage = null): bool
{
// Only throw when a message is provided, or use the default
// behaviour provided by policies
if (!$hasPermission && $allowExceptions && !is_null($exceptionMessage)) {
throw new \App\Exceptions\AuthorizationException($exceptionMessage);
}
return $hasPermission;
}
New exception for throwing in policies only in \App\Exceptions:
namespace App\Exceptions;
use Exception;
/**
* The AuthorizationException class is used by policies where authorization has
* failed, and a message is required to indicate the type of failure.
* ---
* NOTE: For consistency and clarity with the framework the exception was named
* for the similarly named exception provided by Laravel that does not stop
* execution when thrown in a policy due to internal handling of the
* exception.
*/
class AuthorizationException extends Exception
{
private $statusCode = 403;
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct($message, $code, $previous);
}
public function getStatusCode()
{
return $this->statusCode;
}
}
Handle the exception and provide the message in a JSON response in Handler::render():
public function render($request, Exception $exception)
{
if ($exception instanceof AuthorizationException && $request->expectsJson()) {
return response()->json([
'message' => $exception->getMessage()
], $exception->getStatusCode());
}
return parent::render($request, $exception);
}
and I also removed it from being logged in Handler::report.
What I found was not "passing" a custom message to authorize, just defining a custom message in the policy it selfs, so, for example, if you have the method "canUseIt", in your UserPolicy, like the following:
public function canUseIt(User $user, MachineGun $machineGun)
{
if ($user->isChuckNorris()) {
return true;
}
return false;
}
You can change it and do something like this:
public function canUseIt(User $user, MachineGun $machineGun)
{
if ($user->isChuckNorris()) {
return true;
}
$this->deny('Sorry man, you are not Chuck Norris');
}
It uses the deny() method from the HandlesAuthorization trait.
Then when you use it like $this->authorize('canUseIt', $user) and it fails, it will return a 403 HTTP error code with the message "Sorry man, you are not Chuck Norris".
Laravel does have an option to pass arguments to customize the errors in the authorize() method of a Controller Class accessed through the Gate Class's implementation of the GateContract made available by the Gate Facade.
However, it seems that they forgot to pass those arguments to the allow()/deny() methods responsible for returning error messages, implemented in the HandlesAuthorization Trait.
You need to pass those arguments by following these steps:
Modify the authorize method in the vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php file
public function authorize($ability, $arguments = []) {
$result = $this->raw($ability, $arguments);
if ($result instanceof Response) {
return $result;
}
return $result ? $this->allow() : $this->deny($arguments);
}
Call authorize from the controller with an extra argument, ie: your custom $message -
$message = "You can not delete this comment!";
$response = $this->authorize('delete', $message);
I have made a pull request to fix this, hopefully someone will merge it soon.
I think the best way to think about Policies is they are simply a way to split controller logic, and move all authorization related logic to a separate file. Thus abort(403, 'message') is the right way to do this, in most cases.
The only downside is you may want some policies to be 'pure' logic for use in business logic only, and thus not to have any response control. They could be kept separate, and a commenting system could be used distinguish them.
Using Laravel 5.1's Queues, I'm throwing an exception when a job fails.
throw new \Exception('No luck');
As Laravel recommends when dealing with failed jobs, I'm "catching" the exception in the AppServiceProvider, and using that to send our team an email.
public function boot()
{
Queue::failing(function ($connection, $job, $data) {
$info['data'] = $data;
\Mail::send('emails.jobs.failed', $info, function($message) {
$message->to('test#email.com')->subject('Job failed');
});
});
}
Within the email, I would like to place the exception's message (in this case "No luck."). But I can't figure out how to pass that along to Queue::failing().
Any ideas?
After calling the failing callback, Laravel rethrows the exception.
It seems if you really need the error message, you'll have to catch the exception yourself.
I'm following Dayle Rees' book "Code Bright" tutorial on building a basic app with Laravel (Playstation Game Collection).
So far so good, the app is working but, following his advices at the end of the chapter, I'm doing my homeworks trying to improve it
So, this snippet is working fine for existing models but throws an error if the item doesn't exists:
public function edit(Game $game){
return View::make('/games/edit', compact('game'));
}
In other words, http://laravel/games/edit/1 shows the item with ID = 1, but http://laravel/games/edit/21456 throws an error since there's no item with that ID
Let's improve this behaviour, adapting some scripts found also here on StackOverflow (Laravel 4: using controller to redirect page if post does not exist - tried but failed so far):
use Illuminate\Database\Eloquent\ModelNotFoundException; // top of the page
...
public function edit(Game $game){
try {
$current = Game::findOrFail($game->id);
return View::make('/games/edit', compact('game'));
} catch(ModelNotFoundException $e) {
return Redirect::action('GamesController#index');
}
}
Well... nothing happens! I still have the error with no redirect to the action 'GamesController#index'... and please notice that I have no namespaces in my Controller
I tried almost anything:
Replace catch(ModelNotFoundException $e) with catch(Illuminate\Database\Eloquent\ModelNotFoundException $e): no way
put use Illuminate\Database\Eloquent\ModelNotFoundException; in Model instead of Controller
Return a simple return 'fail'; instead of return Redirect::action('GamesController#index'); to see if the problem lies there
Put almost everywhere this snippet suggested in Laravel documentation
App::error(function(ModelNotFoundException $e)
{
return Response::make('Not Found', 404);
});
Well, simply nothing happened: my error is still there
Wanna see it? Here are the first two items in the errors stack:
http://www.iwstudio.it/laravelerrors/01.png
http://www.iwstudio.it/laravelerrors/02.png
Please, can someone tell me what am I missing? This is driving me mad...
Thanks in advance!
Here are few of my solutions:
First Solution
The most straightforward fix to your problem will be to use ->find() instead of ->findOrFail().
public function edit(Game $game){
// Using find will return NULL if not found instead of throwing exception
$current = Game::find($game->id);
// If NOT NULL, show view, ELSE Redirect away
return $current ? View::make('/games/edit', compact('game')) : Redirect::action('GamesController#index');
}
Second solution
As I notice you may have been using model binding to your route, according to Laravel Route model binding:
Note: If a matching model instance is not found in the database, a 404 error will be thrown.
So somewhere where you define the model binding, you can add your closure to handle the error:
Route::model('game', 'Game', function()
{
return Redirect::action('GamesController#index');
});
Third Solution
In your screenshot, your App::error seems to work as the error says HttpNotFound Exception which is Laravel's way of saying 404 error. So the last solution is to write your redirect there, though this apply globally (so highly discouraged).