Best way to handle menu not found exception? - php

I'm currently refactoring the menu class in our PHP CMS, and am currently trying to work out the best way to handle the issue where someone tries to create a menu (by passing in the title of the menu (we have main, footer, utility etc menus), but the menu isn't in the database.
If they try to create a menu object with a menu that can be found then there's no problem, I can return the object as requested. If they try to create one that isn't found though, I'm currently throwing an exception (which causes an email to be sent), then creating a blank menu object and returning that. The call to output the menu then works without error, but outputs nothing.
(I've done the above by setting it up so a static method of the menu class is called to create a menu object, which can then handle throwing an exception if necessary and return either the requested menu object, or a blank one).
Hopefully all that makes sense! Is this the best method to take? Or is there a more elegant solution?
Chris
Edit:
Here's the static function which is called to create a menu:
static function makeMenu($id,$breakDepth=1){
// try to create Menu
try {
$menu = new Menu($id, $breakDepth);
}
catch (no_menu_found_exception $e) {
// if we failed to find it, an email should have been sent, and create a blank menu so rest of site works without error
$menu = new Menu("");
}
return $menu;
}
and here's the constructor:
function __construct($id,$breakDepth=1){
$this->treeObject = Doctrine_Core::getTable('CmsMenuItemNew')->getTree();
if ($id == "") {
// if ID supplied is empty, return an empty menu object
$this->rootNode = null;
$this->name = $id;
return;
}
if (is_numeric($id)) {
// check it exists?
$this->rootNode = $id;
$node = Doctrine_Core::getTable('CmsMenuItemNew')->findByDQL("menuid = '".$id."'")->getFirst();
$this->name = $node->menutitle;
if ($this->name == "") $this->rootNode = null;
return;
} else {
$this->name = $id;
// find the menu ID for the supplied name
$table = Doctrine_Core::getTable('CmsMenuItemNew');
$table->setOption("orderBy", "level");
$this->rootNode = $table->findByDQL("menutitle = '$id'")->getFirst()->menuid;
// rootNode with supplied name not found, so look for a branch in the main menu
$this->breakDepth = $breakDepth;
if ($this->rootNode === null) {
throw new no_menu_found_exception("Menu not found: ".$id);
}
}
}
as mentioned - its still in development, so not completely finished yet.

Building a blank object is a nice thing. The right design pattern for that is called SpecialObjects. To be complete your code should return a MenuNotFound object which have the same interface as MenuObject.
Then the way this MenuNotFound objects reacts to the interface entry points is up to you.
This avoid checks on the type of object returned and permits chaining.
For the Exception I personnaly prefer Exception which are only real problems. But in your case if you want to get a mail the exception is not a bad idea, maybe this exception or mail handling could be done in the MenuNotFound init.

Related

Calling methods on object gets 'call to member function on array' error

I inherited this project from my predecessor, and he was way overqualified. A lot of stuff he wrote goes over my head. But as far as vanilla php goes, I'm pretty confident, and can't for the life of me figure out why the application thinks the object I created is an array. Maybe I don't actually know anything. You tell me.
use via\zoom\Bulletin;
use via\zoom\DatabaseConnection;
require_once('includes/config.php');
require_once(CORE .'sql.php');
require_once(CORE . 'model.php');
require_once(CORE . 'bulletin.php');
// If we've passed the validation step we can guarantee we have a valid $active_user
validate();
//run if a page deletion has been requested
if (isset($_GET['delpage'])) {
$del = $_GET['delpage'];
$bulletin = new Bulletin;
$bulletin = Bulletin::get($del);
if(!empty($bulletin))
{
$bulletin->delete();
/*
So.
For some reason, the above object is cast as an array.
If you try to cast it as an object, it defaults to stdClass.
On the left we have a method complaining that it can't work outside of its class. Hard stop, array to method exception.
On the right we have an object with all the right data, but set to the wrong class, so it can't find the delete method at all. Hard stop, undefined method exception.
*/
//this is the workaround, pulled the script straight from the delete method in the model class
/*$dbh = DatabaseConnection::get();
$query_string = "DELETE FROM brochure_generator_bulletin WHERE id = $del";
try {
$dbh->query($query_string);
//return true;
} catch (\Exception $e) {
//return false;
}*/
}
header('Location: bulletins');
exit();
}
Here's the get method from the Bulletin class, extends Model--
public static function get( ...$ids )
{
$matches = parent::get( ...$ids );
foreach( $matches as &$match )
{
$match->content = json_decode( $match->content );
}
return $matches;
}
And here's the delete method from the Model Class:
public function delete()
{
if (isset($this->id)) {
$dbh = DatabaseConnection::get();
$query_string = "DELETE FROM {$this->table_name} WHERE id = \"{$this->id}\"";
try {
$dbh->query($query_string);
return true;
} catch (\Exception $e) {
return false;
}
}
return false;
}
What am I missing? Is he using a framework I'm not familiar with? I'm utterly grasping at straws here, and at this point my options are grab all the method scripts and stick them where they need to be inline, or just starting over from the ground up.
You don't need to create a new Bulletin object before using the static get() method, so you can remove this:
$bulletin = new Bulletin;
That $bulletin variable is immediately overwritten by the next line anyway.
$bulletin = Bulletin::get($del);
get() takes one or more ids and returns an array of one or more corresponding objects. You're giving it one id and expecting one object back, but it's still going to return that object inside an array. You just need to get the object out of the array so you can call its delete method.
if(!empty($bulletin))
{
$bulletin = reset($bulletin); // get the first item in the array
$bulletin->delete();
You could also review the model and see if it has a different method that returns a single object rather than an array of objects.

Getting the XML from a response using the Authorize.Net PHP SDK

I have written some functions using Authorize.Net's PHP SDK's that look like the following:
public function getCustomerProfiles() {
$customerProfiles = array();
// Before we can get customer profiles, we need to get a list of all customer id's.
$customerIdListRequest = new AnetAPI\GetCustomerProfileIdsRequest();
$customerIdListRequest->setMerchantAuthentication(self::getMerchantAuth(Config::LOGIN_ID, Config::TRANSACTION_KEY));
$customerIdListController = new AnetController\GetCustomerProfileIdsController($customerIdListRequest);
$customerIdListResponse = $customerIdListController->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);
if(($customerIdListResponse != null) && ($customerIdListResponse->getMessages()->getResultCode() == "Ok")) {
// TODO: Investigate warning about no method named getIds().
foreach( $customerIdListResponse->getIds() as $id ) {
// Now we can get each customer profile.
$request = new AnetAPI\GetCustomerProfileRequest();
$request->setMerchantAuthentication(self::getMerchantAuth(Config::LOGIN_ID, Config::TRANSACTION_KEY));
$request->setCustomerProfileId($id);
$controller = new AnetController\GetCustomerProfileController($request);
$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);
if(($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
// TODO: Investigate warning about no method named getProfile()
// Add it to the array.
array_push($customerProfiles, $response->getProfile()->xml);
} else {
throw new \Exception($response->getMessages()->getMessage());
}
}
} else {
throw new \Exception($customerIdListResponse->getMessages()->getMessage());
}
return $customerProfiles;
}
Currently, I'm just returning an array of objects. I'd prefer to get the raw XML response. Is this functionality available via Authorize.Net's PHP SDK? Or am I better of using something like Guzzle and making the request manually?
Looking at the source code I think it would be simple enough.
Look the execute method that is invoked by executeWithApiResponse there. See xmlResponse? Just need to store that as a class property (and add a public getter), or maybe tweak the function to take an extra argument telling it to return the raw response. Could hack it, or better yet, extend that ApiOperationBase class (note the interface IApiOperation gives you a outline to follow).
Seeing that serializer also...
$this->apiResponse = $this->serializer->deserialize( $xmlResponse, $this->apiResponseType , 'xml');
Could maybe do something more elegant with that. But not as clear as path I first described.

class_exists when autoload flag is false

So, as part of a project, I was considering building a flagging system. The idea behind this would be a cron job that runs daily to determine whether each of a series of flags still applied to a specific object (and if so, save that flag data for the object).
// code stub
$flags = $this->getFlags();
foreach($flags as $flag)
{
$className = 'Svc_Flags_'.$flag->flag_code;
if(class_exists($className, false)
{
(new $className())->setFlag();
}
}
And right now, in the dummy code for that class, I have a constructor that echos a simple text message, and the function setFlag() that echos a different text message.
<?php class Svc_Flags_Test extends Svc
{
public function __construct()
{
echo 'construct<br/>';
}
public function setFlag()
{
echo 'set flag<br/>';
}
}
Now, this doesn't work. By that, I mean that I am not seeing either echo.
However, if I do this:
// code stub
$flags = $this->getFlags();
foreach($flags as $flag)
{
$className = 'Svc_Flags_'.$flag->flag_code;
(new $className())->setFlag(); // This is the added line of code
if(class_exists($className, false)
{
(new $className())->setFlag();
}
}
I get the constructor echo, and the setFlag() echo TWICE.
Why is this happening? Now, I'm pretty sure I could just wrap part of this in a try/catch block to get past any errors if a class isn't there, but I'm curious as to why it doesn't seem to find the class unless I explicitly call it before the if statement.

Using JToolbar or JToolbarHelper for CRUD in Joomla frontend for the custom component

For which cases are these classes suitable? I've been trying to use both, but none of them works.
The component skeleton was generated, and there are CRUD operations in the administrator's side. I tried using JToolbarHelper from this generated code, like this in mycomponent/view.html.php:
// Overwriting JView display method
function display($tpl = null)
{
// Include helper submenu
InvoiceHelper::addSubmenu('invoice');
// Assign data to the view
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
// Check for errors.
if (count($errors = $this->get('Errors'))){
JError::raiseError(500, implode('<br />', $errors));
return false;
};
// Set the toolbar
$this->addToolBar();
// Show sidebar
$this->sidebar = JHtmlSidebar::render();
// Display the view
parent::display($tpl);
}
protected function addToolBar()
{
JLoader::register('JToolbarHelper', JPATH_ADMINISTRATOR.'/includes/toolbar.php');
$canDo = InvoiceHelper::getActions();
JToolBarHelper::title(JText::_('Invoice Manager'), 'invoice');
if($canDo->get('core.create')){
JToolBarHelper::addNew('invoic.add', 'JTOOLBAR_NEW');
};
if($canDo->get('core.edit')){
JToolBarHelper::editList('invoic.edit', 'JTOOLBAR_EDIT');
};
if($canDo->get('core.delete')){
JToolBarHelper::deleteList('', 'invoice.delete', 'JTOOLBAR_DELETE');
};
}
But it doesn't even appear on the page.
Then I came across this tutorial http://docs.joomla.org/J3.x:Using_the_JToolBar_class_in_the_frontend and it kindof works, except I can't imagine implementing something like a list of entities with checkboxes and operations for each. And it's unclear for me how to handle form submissions using this approach, seems like it happens through JS, do I get it right?
So, please tell, what's the difference and why doesn't the first approach even make the toolbar appear?
I know this is a long time ago, but I was looking to achieve the same result and found the following to loads the toolbar on the page. Using the above code:
protected function addToolBar()
{
JLoader::register('JToolbarHelper', JPATH_ADMINISTRATOR.'/includes/toolbar.php');
$canDo = InvoiceHelper::getActions();
JToolBarHelper::title(JText::_('Invoice Manager'), 'invoice');
if($canDo->get('core.create')){
JToolBarHelper::addNew('invoic.add', 'JTOOLBAR_NEW');
}
if($canDo->get('core.edit')){
JToolBarHelper::editList('invoic.edit', 'JTOOLBAR_EDIT');
}
if($canDo->get('core.delete')){
JToolBarHelper::deleteList('', 'invoice.delete', 'JTOOLBAR_DELETE');
}
$this->toolbar = JToolbar::getInstance(); // <<<---------- ADD THIS TO METHOD!
}
Then in your view do this:
<?php echo $this->toolbar->render(); ?>
Hope this helps!!! enjoy.

Is this correct use of Exception handling in PHP / Symfony2

I'm creating a service to fetch some user data
class ExampleService{
// ...
public function getValueByUser($user)
{
$result = $this->em->getRepository('SomeBundle:SomeEntity')->getValue($user);
if (!$result instanceof Entity\SomeEntity) {
throw new Exception\InvalidArgumentException("no value found for that user");
}
return $result;
}
}
Then in my controller I have
// ...
$ExampleService = $this->get('example_serivce');
$value = $ExampleService->getValueByUser($user);
Should I be using an exception here to indicate that no value was found for that user in the database?
If I should, how do I handle what is returned from $ExampleService->getValueByUser($user) in the controller - let's say I just want to set a default value if nothing is found (or exception returned)
Here is how I do it. Let's use a user service and a controller as an example. It's not an exceptional condition in the service layer — it just returns the result without checking it:
class UserService
{
public function find($id)
{
return $this->em->getRepository('UserBundle:User')->find($id);
}
}
But in the controllers layer I throw an exception if the requested user not found:
class UserController
{
public function viewAction($id)
{
$user = $this->get('user.service')->find($id);
if (!$user) {
throw $this->createNotFoundException(
$this->get('translator')->trans('user.not_found')
);
}
// ...
}
}
Where you want to handle the exception is kind of up to you, however I would handle it in the controller (and throw it in the model). I usually try to call a different template if there is an error so as to avoid a bunch of conditionals, but sometimes you just have to put extra logic in your template instead.
Also, you have to ask yourself if this is really an exceptional condition - it might be easier to return null and handle that return value in your controller. I can't really tell from the data objects (value, service, and user) whether this is something that will happen all the time or not.

Categories