Page 1 of 1

Error handling with MVC and PHP4 ideas

Posted: Wed May 24, 2006 12:33 pm
by Nathaniel
Hi guys,

I've been trying to understand MVC with sites like phpPatterns. I haven't got any books yet, but I think I have the basic idea. So anyway, below is my attempt at an MVC for an update profile script. I decided the Front Controller is Apache coupled with mod_rewrite, and the Page Controller is update-profile.php coupled with an UpdateProfileController class.

I don't think I could have gone wrong with my design, since it's so shallow right now, but I'm frustrated with how to deal with errors. The code right now:

Code: Select all

<?PHP
#update-profile.php
$request = new Request(); //give it post, get, whatnot
$response = new Response();
$controller = new UpdateProfileController();

if ( isAnError( $error = $controller->checkUserPermissions( 
	new UserPermissions( $request->getCredentials() ) 
	) )
   )
{
	$response->setHeader( $error->getHeader() );
	$response->setContent( $error->getView() );
	$response->send();
	exit;
}

$controller->setModel( 
	new UpdateProfileModel( $request->getPostData() ) 
);

$controller->setView ( new UpdateProfileView() );

//response will get the correct template output from the UpdateProfileView() in getView()...
//this way, we could come back later and have it output XML or something
//simply by telling $response we want XML output back
//and the response would ask the view object for XML output
$response->setContent( $controller->getView() ); 
$response->send();
exit;
?>
Classes and Functions:

Code: Select all

class PageController()
{
	/* setModel, setView, getView... */
}

class UpdateProfileController extends PageController
{
	function checkUserPermissions($permissions)
	{
		if ( $permissions->isUserAuthenticated() == false )
		{
			return new UnauthenticatedUserError(); //would extend class Error, which would extend class PageController
		}
		
		return true;
	}
}

function isAnError($object)
{
	return is_a( (object) $object, 'Error');
}
Obviously, I want a try / catch / exception setup there, but I'm still stuck with PHP4.
I don't want to use any local handling inside the checkUserPermissions() method, because

1. I'd possibly have to globalize $response inside the class, or give it the $response object,
2. I couldn't "ignore" the exception without modifying the Controller class, and
3. It'd mess up the code flow in update-profile.php, because it wouldn't be obvious how we're dealing with non-credentialed users by just looking at update-profile.php. You'd have to go look at the innards of UpdateProfileController.

Still, the "if ( isAnError( $error = $controller->checkUserPermissions( new UserPermissions( $request->getCredentials() ) ) )" line seems totally clunky.

Any ideas or thoughts?

- Nathaniel

Posted: Wed May 24, 2006 4:09 pm
by Christopher
I think my question would be what is UpdateController for? If your script is a Page Controller then you could just do this:

Code: Select all

#update-profile.php
$request = new Request(); //give it post, get, whatnot
$response = new Response();
$permissions = new UserPermissions( $request->getCredentials() );

$error = $permissions->checkUserPermissions();
if ( $error->isAnError() ) {

        $response->setHeader( $error->getHeader() );
        $response->setContent( $error->getView() );
        $response->send();

} else {

        $view =new UpdateProfileView(new UpdateProfileModel( $request->getPostData() );
        $response->setContent( $view->render() );
        $response->send();
}

Posted: Wed May 24, 2006 4:32 pm
by Nathaniel
Hmm, that does make more sense. I implemented the PageController class to try to make for more reusability, but I guess I wasn't getting any anyway. Thanks.

Posted: Wed May 24, 2006 4:36 pm
by Christopher
Nathaniel wrote:I implemented the PageController class to try to make for more reusability
If you want reusability then roll up everything that is the same in every Page Controller page into a class -- that might include creating the request, response, model and view, and connecting them all together in the right way. The you just extend the Page Controller class and it users class_name($this) . 'Model' and class_name($this) . 'View' to create what it needs

Posted: Wed May 24, 2006 6:39 pm
by Nathaniel
Yah, I could. I'll set it aside for a while and think about how I want to tackle this. Ok, thanks aborint :)

Posted: Thu May 25, 2006 11:00 am
by Christopher
My suggestion would be to instead take the plunge and pull out the following code to a Front Controller:

Code: Select all

$request = new Request(); //give it post, get, whatnot
$response = new Response();

$controller = new // CREATE CONTROLLER HERE

if ( isAnError( $error = $controller->checkUserPermissions(
        new UserPermissions( $request->getCredentials() )
        ) )
   )
{
        $response->setHeader( $error->getHeader() );
        $response->setContent( $error->getView() );
        $response->send();
        exit;
}


// DISPATCH $controller HERE (injecting $request, $response, etc.)


$response->setContent( $controller->render() );
$response->send();
In the long run you won't be sorry.

Posted: Thu May 25, 2006 1:20 pm
by Nathaniel
Ok, you're right. I wanted to use controller-script.php so the scripts could reflect the information hierarchy; but I see now that controller.class.php is going to be more modular, testable and reusable than controller-script.php. Cool stuff.

For anyone who was wondering how I got rid of the clunky isAnError line:

Code: Select all

// Put this on its own line, for one
$permissions = new UserPermissions( $request->getAuthData() );

// Note the double equals. checkUserPermissions() returns boolean true if the permissions are OK, or an error controller object if not.
// If you didn't check for type as well as value, the if block contents would never execute
// This is a lot more intuitive than isAnError(), imo
// Of course, anything is going to be a smelly workaround for not having try/catch
if ( ( $error = $controller->checkUserPermissions( $permissions ) ) !== true )
{
	$response->setHeaders( $error->getView()->getHeaders() );
	$response->setContent( $error->getView()->render() );
	$response->send();
	exit;
}
Thanks, aborint.

- Nathaniel