different exceptions for each controller cakephp - php

I want to create exceptions for each controller in my project with cakephp. Example:
class UsersController extends AppController {
public function add(){
if(!$this->User->save($this->request->data)){
throw new UserException('Error save User/add');
}
}
}
class FriendsController extends AppController {
public function add(){
if(!$this->Friend->save($this->request->data)){
throw new FriendException('Error save Friend/add');
}
}
}
I'm trying this, but does not work...never paint the log in class exception user/friend
In app/Config/core.php
Configure::write('Exception.handler', 'UserException::handle');
Configure::write('Exception.handler', 'FriendException::handle');
In app/Config/bootstrap.php
App::uses('UserException', 'Lib');
App::uses('FriendException', 'Lib');
In app/Lib/UserException.php
class UserException {
public static function handle($error) {
$this->log($error)
}
}
In app/Lib/FriendException.php
class FriendException {
public static function handle($error) {
$this->log($error)
}
}
Any idea?
Regards!

An exception for each controller seems quite redundant. Why do you need 2 exception classes UserException and FriendException when they are identical, only difference being the message you pass? Also logging feature is inbuilt in core's ExceptionHandler. You can just throw a CakeException or RuntimeException with required message.
Do you realize your code:
Configure::write('Exception.handler', 'UserException::handle');
Configure::write('Exception.handler', 'FriendException::handle');
is overwriting the config same variable?
Anyway the 'Exception.handler' config is meant to specify the class with handles ALL your exceptions. You can confusing an exception class with an error handler class. Exception classes don't have any handle() method. Your exception classes should extend CakeException and override required method/property.

Use the CakeDC Utils plugin, it comes with an EmailErrorHandler that is configurable and can send an email depending on the error level.

Related

How to make PHPStan understand exception handling for child methods?

TL;DR
My questions being :
Is it a good practice to have exceptions inside an overriden methods ?
If so, how should I document and handle exceptions so that PHPStan doesn't block my pipelines ?
If not, how is the best way(s) to handle this kind of cases ?
Full context
I have some trouble regarding Handling Exception with PHPStan.
I have this abstract class :
abstract class GenericClass {
final public function handle(): void {
$this->myMethod();
}
abstract protected function myMethod(): void;
}
and this child class :
class MyClass extends GenericClass {
/**
* #throws MyException
**/
protected function myMethod(): void
{
throw new MyException();
}
}
Finally, I inject this child class inside a service class :
class ServiceClass {
private MyClass $myClass;
protected function __construct(MyClass $myClass)
{
$this->myClass = $myClass;
}
public function handle(): void
{
try {
$this->myClass->handle();
} catch(MyException $e) {
//error handling here
}
}
}
Obviously, abstract method myMethod cannot guess what kind of exception will be thrown (if any). It all a matter of context. Only the calling Service with the corresponding class can know the context in which the exception is thrown.
However, when PHPStan run through this code, it says that my catch is a dead code because it's never thrown.
This is a simple case with not many exception thrown but on a larger scale with multiple child classes and different exceptions thrown on these overriden methods, it could be time-consuming to update PHPDoc on parent method.
How do I make PHPStan understand that it is not dead code.
I only want to add #phpstan-ignore-next-line if no other solutions is possible.
Thanks in advance for all your replies

Laravel 8 - Using custom exceptions

I have a question about error reporting in Laravel 8.
According to the documentation, it should be possible to implement custom report() or render() methods in a custom exception.
I have created a custom exception as follows:
class AdminException extends Exception
{
protected $userId;
public function __construct($message='', $userId=0) {
parent::__construct($message);
$this->setUserId($userId);
}
public function getUserId() {
return $this->userId;
}
private function setUserId($userId) {
return $this->userId = $userId;
}
public function report(){
dd('in custom admin exception report');
}
public function render($request){
dd('in custom admin exception render');
}
}
The issue is, when I throw this error intentionally from a controller, I am not hitting any of these methods in the exception.
To try and compensate for that problem, I added methods in the Handler.php file as follows:
$this->reportable( function(AdminException $e){
dd('in reportable method from handler');
});
$this->renderable( function(AdminException $e, $request){
dd('in renderable method from handler');
});
but neither of these methods are being hit either. Does anyone have experience with custom exceptions in Laravel 8? The documentation is very unclear, how is this meant to work? Should I just use the report() and render() methods of the Handler.php file?
Thanks!
I am answering this in case anyone else stumbles upon this problem.
After tweeting back and forth with Taylor Otwell himself, I got some clarification on the subject.
When using the Hander.php file in Laravel, you can use the report() method but it is important to call parent::report($e) before doing anything else. Without doing this, the reporting logic will not be hit.
However, the preferred behavior is to not use the report() method in Handler.php but instead to register report or render callbacks in the register() method of Handler.php.
Just to clarify, it is possible to implement the report() method in Handler.php as a fallback but the parent::report($e); method must be called in the report() method.

Trait in CodeIgniter

I'm trying to use traits in CodeIgniter. I put the Trait in the libraries directory and then loaded the library in the controller. Then I used the trait in the model, but it didn't work and I received the following error
Trait file (in library):
trait DataTablesTrait{
public function query(){
$this->db->select('*');
$this->db->limit(10, 1);
return $this->db->get('my_table');
}
}
?>
Controller:
class myController extends CI_Controller {
public function __construct(){
parent::__construct();
$this->load->library('DataTablesTrait');
$this->load->model('ABC_Model');
}
}
Model:
class ABC_Model extends CI_Model {
use DataTablesTrait;
function callQuery(){
$this->query();
}
}
I got this error:
Non-existent class: DataTablesTrait
Please advise
CodeIgniter (CI) isn't trait or namespace friendly but they can be used. It requires working around CI a bit though.
The main problem, the CI thing you have to work around, is the call
$this->load->library('DataTablesTrait');
CI will find this file but load->library will try to instantiate the trait which fails because it is not possible to instantiate a Trait on its own.
Replace the line above with
include APPPATH.'/libraries/DataTablesTrait.php';
You should be free of the error with that. But you're not going to get results because callQuery() does not return anything. To round out the test have callQuery() return the CI_DB_result object the Trait should produce
public function callQuery()
{
return $this->query();
}
Add an index function to the controller so we can dump the output
public function index()
{
$DB_result = $this->ABC_Model->callQuery();
var_dump($DB_result->result());
}
This should produce the expected output - assuming that 'my_table' has data :)
Worth saying that traits are now fully supported in Codeigniter 4 and can be implemented as you would expect using namespaces etc.
// TRAIT
namespace App\Controllers\traits;
trait YourTraitName {
}
// CONTROLLER
use App\Controllers\traits\YourTraitName;
class Admin extends BaseController {
use YourTraitName;

Why can't CodeIgniter autoload my Exceptions file in application/core?

I have an exception in application/core named prefix_Exceptions.php with the same class name. I try to throw this exception from a controller and I get:
Fatal error: Class 'prefix_Exceptions' not found in user_controller.php
In application/core/prefix_Exceptions.php:
<?php
class prefix_Exceptions extends CI_Exceptions {
public function __construct() {
parent::__construct();
}
public function test() {
echo "This is a test.";
}
}
And in application/controllers/user_controller.php:
<?php
class User_Controller extends CI_Controller {
public function view($id = '0') {
$this->load->model('user_model');
$u = $this->user_model->getUser($id);
if (!isset($u)) {
$this->exceptions->test(); // ???
}
echo "Test: $u";
}
}
Oh, and my prefix is set to prefix_:
$config['subclass_prefix'] = 'prefix_';
I've read about a dozen threads on this issue and none of them fix my exception so that it can be thrown by the controller.
The main reason your code is not working, is (as the error message suggests): your prefix_invalid_user.php is never loaded. CI does not know to load this file, as you are not following the required file naming scheme.
If you want to extend a built-in class, you have to use the same class name, except you change the prefix from CI_ to MY_ (or whatever prefix you set in your config).
To extend the class CI_Exceptions you would have to name it MY_Exceptions and save that php file in /application/core/MY_Exceptions.php. Then, and only then, will CI auto-load it for you.
However you should also know that CI's exceptions class isn't actually for throwing exceptions (the name is misleading, but CI_Exceptions handles error reporting). As you can see in the /system/core/Exceptions.php file, the CI_Exceptions class does not extend PHP's native Exceptions class, which is necessary to create custom, throwable exceptions.
If you want custom, throwable exceptions you have to create your own wrapper for them, and load/autoload it as a library.
Edit:
As per the OP's request, I'm adding the other half of the solution, which was to simply fetch the class object from CI's innards. For this, we can use the load_class function, which will return our class object if it has been instantiated, and if not, it will instantiate and return it.
$foo = load_class('Exceptions', 'core', $this->config->item('subclass_prefix'))
Then we can access the methods of our custom Exceptions class as so:
$foo->someMethodName();

Set validation message

I am trying to set a validation message for a set_rule....is this possible?
I tried this
$this->form_validation->set_rules('payment_amount', 'Payment Amount', 'regex_match[/^\d{0,4}(\.\d{1,2})?$/]');
$this->form_validation->set_message('payment_amount', 'This is a test');
and my message did not change.
Any ideas?
You can set a custom validation error message by creating a function for the rule.
This code is untested.
public function payment_amount($str)
{
if (preg_match('[/^\d{0,4}(\.\d{1,2})?$/]', $str)
{
$this->form_validation->set_message('payment_amount', 'The %s field has an error');
return FALSE;
}
else
{
return TRUE;
}
}
Us CI's callback as follows:
$this->form_validation->set_rules('payment_amount', 'Payment Amount', 'callback_payment_amount')
Create MY_Form_validation.php & put your code into.
Use the documentation (http://ellislab.com/codeigniter/user-guide/general/creating_libraries.html)
Extending Native Libraries
If all you need to do is add some functionality to an existing library - perhaps add a function or two - then it's overkill to replace the entire library with your version. In this case it's better to simply extend the class. Extending a class is nearly identical to replacing a class with a couple exceptions:
The class declaration must extend the parent class.
Your new class name and filename must be prefixed with MY_ (this item is configurable. See below.).
For example, to extend the native Email class you'll create a file named application/libraries/MY_Email.php, and declare your class with:
class MY_Email extends CI_Email {
}
Note: If you need to use a constructor in your class make sure you extend the parent constructor:
class MY_Email extends CI_Email {
public function __construct()
{
parent::__construct();
}
}

Categories