CakePHP exception logic - cakephp

I have a controller where I throw a custom exception and I have a custom exception renderer class, which extends the basic exception renderer.
Now when I throw the exception, I'd like to do some cleanup with the stuff, that went wrong and after that render a custom error page.
class AppExceptionRenderer extends ExceptionRenderer {
public function invalidCall($error) {
$this->controller->render('/Errors/invalid_call');
$this->controller->response->send();
}
public function incompleteCall($error) {
$this->controller->render('/Errors/incomplete_call');
$this->controller->response->send();
}
}
The rendering works well so far. But where should I put the logic for the cleanup things?
In the exception itself? In the renderer? In the controller before throwing the exception?

Well, as so often there are many ways to skin a cat, but I'd say in order to stay DRY, for easy testing, and in order to stay in compliance with the recommended fat model concept, you should put the logic in the model.
And in order to decouple cleanup and exception handling, you could for example utilize the event system and let the models that may need to be cleaned attach themselfs as listeners (they should know best whether they may need to be cleaned up), and let a custom error handler dispatch an appropriate event, that way the exception handler doesn't need to know about the app internals.
Here's some very basic, untested example code that should illustrate the idea:
<?php
App::uses('CakeEventManager', 'Event');
class ExampleModel extends AppModel
{
public $name = 'Example';
public function __construct($id = false, $table = null, $ds = null)
{
CakeEventManager::instance()->attach(array($this, 'cleanup'), 'AppErrorHandler.beforeHandleException');
parent::__construct($id, $table, $ds);
}
public function cleanup()
{
// do some magic
}
}
?>
<?php
App::uses('CakeEvent', 'Event');
App::uses('CakeEventManager', 'Event');
class AppErrorHandler extends ErrorHandler
{
public static function handleException(Exception $exception)
{
CakeEventManager::instance()->dispatch(new CakeEvent('AppErrorHandler.beforeHandleException', get_called_class(), array($exception)));
parent::handleException($exception);
}
}
?>
Update
In order to be able to react to specific exceptions only, you could for example utilize the exception class name in the event name, so it would trigger events like ...beforeHandleFooBarException to wich you could explicitly subscribe:
<?php
class AppErrorHandler extends ErrorHandler
{
public static function handleException(Exception $exception)
{
CakeEventManager::instance()->dispatch(new CakeEvent('AppErrorHandler.beforeHandle' . get_class($exception), get_called_class(), array($exception)));
parent::handleException($exception);
}
}
?>
<?php
class ExampleModel extends AppModel
{
public $name = 'Example';
public function __construct($id = false, $table = null, $ds = null)
{
$eventManager = CakeEventManager::instance();
$callback = array($this, 'cleanup');
$eventManager->attach($callback, 'AppErrorHandler.beforeHandleInvalidCallException');
$eventManager->attach($callback, 'AppErrorHandler.beforeHandleIncompleteCallException');
parent::__construct($id, $table, $ds);
}
public function cleanup()
{
// do some magic
}
}
?>
If you would stick with the generic exception event, then another option would be to check the type of the exception in the models event listener callback:
public function __construct($id = false, $table = null, $ds = null)
{
CakeEventManager::instance()->attach(array($this, 'beforeHandleException'), 'AppErrorHandler.beforeHandleException', array('passParams' => true));
parent::__construct($id, $table, $ds);
}
public function beforeHandleException($exception)
{
if($exception instanceof InvalidCallException ||
$exception instanceof IncompleteCallException)
{
$this->cleanup();
}
}
public function cleanup()
{
// do some magic
}

Related

Laravel ORM relation return data as array instead of object

I was trying to get the relational model data like
{{$OrderInfo->CustomerInfo->Phone}}
but it's giving error like
Trying to get property of non-object
While we can easily access the returned data like
{{$OrderInfo->CustomerInfo['Phone']}}
My work is temporarily working, but I was not satisfied. It should worked to access the data as an object. Because, I think that is right process to access the data. Please can anyone help me to come out from the problem.
Thanks so much in advance for your valuable time!
Order, Customer Model & My Controller Code given below
use Carbon\Carbon;
use App\OrderInfo;
use App\CustomerInfo;
use Mail;
class AdminOrderController extends Controller
{
public function index(Request $request)
{
$Orders = OrderInfo::orderBy('OrderDate', 'DESC')->get();
return view('admin.admin-order-list', [
'Orders' => $Orders,
]);
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class OrderInfo extends Model
{
protected $table = 'order_info';
public $timestamps = false;
protected $primaryKey = 'OrderId';
public function CustomerInfo()
{
return $this->belongsTo('App\CustomerInfo', 'CustomerID');
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class CustomerInfo extends Model
{
protected $table = 'customer_info';
public $timestamps = false;
protected $primaryKey = 'CustomerID';
protected $fillable = ['CustomerID','Phone'];
public function OrderInfo()
{
return $this->belongsTo('App\OrderInfo', 'CustomerID');
}
}

Laravel Custom Pivot Table relationship and eager loading?

I am having problems with creating a schema / model for one of my projects and would like to get some help here.
I have 3 tables currently : accessories , products and a pivot table product_accessory
<?php
Schema::create('accessories', function(Blueprint $table)
{
$table->increments('id');
}
Schema::create('products', function(Blueprint $table)
{
$table->increments('id');
}
Schema::create('product_accessory', function(Blueprint $table)
{
$table->increments('id');
$table->integer('product_id')->unsigned();
$table->integer('accessory_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products');
$table->foreign('accessory_id')->references('id')->on('accessories');
}
Now problem is I need to add another product type 'adaptors' that would ultimately depend on the pivot table relation, that is, adaptors need to relate to both a product and accessory...
UPDATE
Here is how my current product_accessory_adaptor table is
Schema::create('product_accessory_adaptor', function(Blueprint $table)
{
$table->increments('id');
$table->integer('product_accessory_id')->unsigned();
$table->foreign('product_accessory_id')->references('id')->on('product_accessory');
}
This way, i can have many adaptors relating to a product and accessory. My question is how do i model this relation in eloquent?
Here's what i have now:
Custom pivot model :
class ProductAccessory extends Pivot {
protected $table = 'product_accessory';
public function product()
{
return $this->belongsTo('Product');
}
public function accessory()
{
return $this->belongsTo('Accessory');
}
public function adaptors() {
return $this->hasMany('Adaptor', 'product_accessory_id');
}
}
Product and Accessory model
class Accessory extends Eloquent {
public function products()
{
return $this->belongsToMany('Product', 'product_accessory', 'accessory_id', 'product_id')->withPivot();
}
public function newPivot(Eloquent $parent, array $attributes, $table, $exists)
{
if ($parent instanceof Product) {
return new ProductAccessory($parent, $attributes, $table, $exists);
}
return parent::newPivot($parent, $attributes, $table, $exists);
}
public function adaptors()
{
return $this->hasManyThrough('Adaptor', 'ProductAccessory', 'accessory_id', 'product_accessory_id');
}
}
class Product extends Eloquent {
public function accessories()
{
return $this->belongsToMany('Accessory', 'product_accessory', 'product_id', 'accessory_id')->withPivot();
}
public function newPivot(Eloquent $parent, array $attributes, $table, $exists)
{
if ($parent instanceof Accessory) {
return new ProductAccessory($parent, $attributes, $table, $exists);
}
return parent::newPivot($parent, $attributes, $table, $exists);
}
public function adaptors()
{
return $this->hasManyThrough('Adaptor', 'ProductAccessory', 'product_id', 'product_accessory_id');
}
}
Adaptor model:
class Adaptor extends Eloquent {
protected $table = 'product_accessory_adaptor';
public function productAccessory() {
return $this->belongsTo('ProductAccessory');
}
}
Update
Now the schema and model is setup. However there are issues with using hasManyThrough relations. In addition, any way to do eager loading in this case for the pivot relation , i.e Adaptor?
Note
The error that occurs when i make a call to adaptors() on either the Product or Accessory model is
Argument 1 passed to Illuminate\Database\Eloquent\Relations\Pivot::__construct() must be an instance of Illuminate\Database\Eloquent\Model, none given, called in /vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 872 and defined
That's your pivot:
<?php
use Illuminate\Database\Eloquent\Model as Eloquent;
// you don't need to call it ..Pivot, just my suggestion
class ProductAccessory extends Eloquent {
protected $table = 'product_accessory';
public function product()
{
return $this->belongsTo('Product');
}
public function accessory()
{
return $this->belongsTo('Accessory');
}
public function adaptors()
{
return $this->hasMany('Adaptor', 'product_accessory_id');
}
}
// Product model
public function adaptors()
{
return $this->hasManyThrough(
'Adaptor', 'ProductAccessoryPivot', 'product_id', 'product_accessory_id'
);
}
// Accessory model
public function adaptors()
{
return $this->hasManyThrough(
'Adaptor', 'ProductAccessoryPivot', 'accessory_id', 'product_accessory_id'
);
}
Now example usage:
$product = Product::first();
$product->adaptors; // collection of all adaptors for given product
$product->adaptors->first()->accessory; // accessory for single adaptor
$product->accessories; // collection of accessories, each with your custom pivot, so:
$product->accessories->first()->adaptors; // collection of adaptors for given product-accessory pair
... and more, try it

CakePHP 2.3.0 component $controller is not accessible

I am using Cakephp 2.3.0, loading following component.
class BreadCrumbsComponent extends Component {
public $components = array();
public $controller = null;
public function initialize($controller) {
}
public function startup($controller) {
$this->controller = $controller;
}
public function beforeRender($controller) {
}
public function shutDown($controller) {
}
public function beforeRedirect($controller, $url, $status = null, $exit = true) {
}
public function handle($controllerName = NULL, $actionName = NULL) {
pr($this->controller->modelClass);
}
}
It get error following error
Trying to get property of non-object [APP\Controller\Component\BreadCrumbsComponent.php, line 38]
I am unable to access $this->controller there. Any reason? How do I make it work?
Read here startup method is called after the controller so need to initialize controller in initialize method as below,
public function initialize(&$controller, $settings = array()) {
$this->controller = $controller;
}

using soap client in model

I should get the available products list and their prices from another server by WSDL (and NuSOAP).
No views is needed (and no controllers I think); So I create a model with no tables (because I don't want to store server data)
And use App:import('Vendor', 'path_to_nusoap.php') at the beginning of my model file.
Let's see my model:
<?php
App::uses('AppModel', 'Model');
App::import('Vendor', 'nusoap' . DS . 'nusoap.php');
/**
* MyModel Model
*
*/
class MyModel extends AppModel {
public $useTable = false;
public $client = new nusoap_client('url', 'WSDL');
public function products(){
$products = $client->call('getProductsList');
////
return $products;
}
public function prices(){
$prices = $client->call('getPricesList');
////
return $prices;
}
}
but it causes an error (on that line: public $client)
Now, the questions:
How to solve that error? (use a contractor function?)
Am I wrong to use this functions on model? (instead of controller)
Sorry for my terrible English.
Thanks.
you cannot create an object outside of a method scope!
use a constructor:
public $Client;
public function __construct() {
$this->Client = new nusoap_client('url', 'WSDL');
}
public function products() {
$products = $this->Client->call('getProductsList');
return $products;
}

cakephp beforeSave flash message

Is there a way to set flash message or error message from the Model, in the beforeSave function and read the error/message in the view. And I'm not talking about validation errors.
Something along these lines should work with the information available on hand:
<?php
class AppModel extends Model {
public $lastErrorMessage;
public function beforeSave(...) {
$this->lastErrorMessage = null;
return true;
}
}
<?php
class MyModel Extends AppModel {
public function beforeSave(...) {
parent::beforeSave(..);
if (error) {
$this->lastErrorMessage = 'Some error message';
return false;
}
return true;
}
}
<?php
class MyController extends AppController {
public function action() {
if ($this->MyModel->save($this->request->data)) {
} else {
$message = "Some default message";
if ($this->MyModel->lastErrorMessage) {
$message = $this->MyModel->lastErrorMessage;
}
$this->Session->setFlash($message);
}
}
}

Resources