I'm wondering is there a better way of "braking out" of method under some conditions. Let me better explain this with code:
function execute($context)
{
// some init actions
$event = new BeforeOperationOne();
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return;
}
$result = $this->operationOne();
// some other code
$event = new BeforeOperationTwo();
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return;
}
// this is not important what is access checker,
// this is just to show that all following code uses data
// computed in previous steps
$accessChecker = new AccessChecker($result);
$this->operationTwo(accessChecker);
// some other code
$event = new BeforeOperationThree();
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return;
}
$this->operationThree();
// some other code
}
We have repeated here the condition, setting user in context when user has access from event. What options I can think about is:
The ugly do-while(false) or goto (I better leave it this way as it is now)
Extract this to method and change the condition to if (!$this->handleEvent($event, $context)) { return; }- This doesn't help to much and cannot think a better name handle doesn't say it's returning something
Build array of closures for operations and loop it through checking. We can assume that all event classes are derived from common class with accessGranted methods. This can be ugly as some operations need data from previous "steps", I would have to keep them outside and pass them.
Throw and catch exceptions that user has access - another bad solution.
Do you have any ideas how to write it better?
#Greg i was thinking about something like that:
abstract class Handler
{
protected $nextHandler = null;
abstract public function Request($request);
public function setNextHandler(Handler $handler)
{
$this->nextHandler = $handler;
}
protected function someOperations($event)
{
//i copied this section, so you must shape that
$this->dispatch($event);
if ($event->accessGranted()) {
$context->setUser($this->user);
// other repeated code
return true;
}
return false;
}
}
class BeforeOperationOneHandler extends Handler
{
public function Request($request)
{
if ($this->someOperations(new BeforeOperationOne())) {
return;
}
$result = $this->operationOne(); // shape this too
return $this->nextHandler->Request($result);
}
}
class BeforeOperationTwoHandler extends Handler
{
public function Request($request)
{
if ($this->someOperations(new BeforeOperationTwo())) {
return;
}
$accessChecker = new AccessChecker($result); // shape this too
$result = $this->operationTwo(accessChecker);
return $this->nextHandler->Request($result);
}
}
class BeforeOperationThreeHandler extends Handler
{
public function Request($request)
{
if ($this->someOperations(new BeforeOperationThree())) {
return;
}
$result = $this->operationThree(); // shape this too
return $this->nextHandler->Request($result);
}
}
class DefaultHandler extends Handler
{
public function Request($request)
{
// this is the last step
}
}
function execute($context)
{
// some init actions
$beforeOperationOneHandler = new BeforeOperationOneHandler();
$beforeOperationTwoHandler = new BeforeOperationTwoHandler();
$beforeOperationThreeHandler = new BeforeOperationThreeHandler();
$defaultHandler = new DefaultHandler();
// set the sequence of the elements
// BeforeOperationOneHandler > BeforeOperationTwoHandler > BeforeOperationThreeHandler> DefaultHandler
$beforeOperationOneHandler->setNextHandler($beforeOperationTwoHandler);
$beforeOperationTwoHandler->setNextHandler($beforeOperationThreeHandler);
$beforeOperationThreeHandler->setNextHandler($defaultHandler);
return $beforeOperationOneHandler->Request($some_init);
}
It's only quickly written shape of "chain of responsibility" pattern so i thoughtlessly copied some of your code fragments
I hope this will lead you to a better solution
Related
NOTE: I have added only snippets of my code.
I have to validate Partner user while they register and if the validation passes then we create a plan for them else we treat them as a normal user and won't create a plan for them.
public function givePlan(Request $request){
/* Validation Code Goes Here */
try{
$data = $request->all();
/*Get partner details*/
$partner = $this->getPartnerDetailsByUniqueId($data['partner_id']);
/** Validate the user details */
$this->validateUser($partner, $data['phone']);
/* Rest of the code */
}catch(\Exception $e){
/* 400 handle the exception */
}
}
protected function validateUser($partner, $phone){
$partnerObj = new ValidatePartner();
if($partner == 'partner1'){
$partnerObj->validatePartner1($phone);
/* rest of the code */
}else if($partner == 'partner2'){
$partnerObj->validatePartner2($phone);
/* rest of the code */
}else if($partner == 'partner3'){
$partnerObj->validatePartner3($phone);
/* rest of the code */
}
}
The problem over here is we need to check the partner with multiple if else condition and then validate the user.
I know we can use the Polymorphism with this and can be achieved by Interface as follows
interface ValidateUser {
public function validate();
}
class Partner1 implements ValidateUser{
public function validate(){
}
}
But again I need to check and create the partner object and the following code still exists
$partnerObj = new Partner1();
$partnerObj->validate($phone);
I had read somewhere that we can pass directly the interface object to achieve this without the object knowledge. Can anyone help me out with this issue?
Any other programming language implementation with Interface or similar feature of PHP is welcome.
Implementing interface having simple function validate.
class partnerFactory {
public static function getType($type) {
switch ($type) {
case 'partner1':
return new Partner1();
case 'partner2':
return new Partner1();
default:
break;
}
}
}
class Partner1 implements ValidateUser {
public function validate() {
// your function here.
}
}
interface ValidateUser {
public function validate() { }
}
$partner1 = partnerFactory::getType('partner1');
$partner1->validate();
$partner2 = partnerFactory::getType('partner2');
$partner2->validate();
I have a PHP/Laravel best practice question.
Here is the scenario:
I have an object with a "type" property in it. The type property will be set to an integer of some kind.
Depending on the "type" integer in the object, different actions will have to happen.
What is the best way to process this object? I am trying desperately to think of a way that avoids using a whole bunch of if/else statements as that feels very like a very wrong and ugly way to approach this.
i.e. I don't want this:
if($obj->type == 1) {
//process actions for type 1
}
else if($obj->type == 2){
//process actions for type 2
}
Would appreciate any advice.
Thanks!
Thanks to #Ryan Vincent I found this resource (https://sourcemaking.com/design_patterns/strategy/php) and changed the Strategy design pattern a bit. For avoiding hard-coded type values check how the dynamic class loading is done in StrategyContext::__construct method. New class instance is initiated by the $type variable name. Class names should be strings in PHP so this way forces types to be strings not only numbers.
Different than the original example in the article, I attached StrategyContext to the book object and wrap the get methods with the strategy to have better use of book object.
Unfortunately if the business logic will be in your code somehow you need to hardcode it. With this method you don't hardcode for each type but you need to create a strategy class for each type. In the example we have StrategyCaps , StrategyStars and StrategyExclaim strategies. So our types are limited to Caps, Stars and Exclaim.
I didn't try this piece of code in production environment but you can have a starting point via the example.
Also for dynamic loading, you can benefit from this question too.instantiate a class from a variable in PHP?
Hope it helps,
<?php
interface StrategyInterface {
public function showTitle($title);
public function showAuthor($author);
}
class StrategyContext implements StrategyInterface {
private $strategy = NULL;
public function __construct($type) {
//Dynamic class loading per type
$classname="Strategy{$type}";
if(class_exists($classname)) {
$this->strategy = new $classname();
} else {
throw new Exception("Strategy not found", 1);
}
}
public function showTitle($title) {
return $this->strategy->showTitle($title);
}
public function showAuthor($author) {
return $this->strategy->showAuthor($author);
}
}
class StrategyCaps implements StrategyInterface {
public function showTitle($title) {
return strtoupper ($title);
}
public function showAuthor($author) {
return strtoupper ($author);
}
}
class StrategyExclaim implements StrategyInterface {
public function showTitle($title) {
return Str_replace(' ','!',$title);
}
public function showAuthor($author) {
return Str_replace(' ','!',$author);
}
}
class StrategyStars implements StrategyInterface {
public function showTitle($title) {
return Str_replace(' ','*',$title);
}
public function showAuthor($author) {
return Str_replace(' ','*',$author);
}
}
class Book {
private $author;
private $title;
private $strategy;
function __construct($strategy, $title_in, $author_in) {
$this->strategy = new StrategyContext($strategy);
$this->author = $author_in;
$this->title = $title_in;
}
function getAuthor() {
return $this->strategy->showAuthor($this->author);
}
function getTitle() {
return $this->strategy->showTitle($this->title);
}
function getAuthorAndTitle() {
return $this->getTitle() . ' by ' . $this->getAuthor();
}
}
writeln('BEGIN TESTING STRATEGY PATTERN');
writeln('');
$type = 'Caps';
$book = new Book($type, 'PHP for Cats','Zeev Suraski');
writeln($book->getAuthorAndTitle());
$type = 'Exclaim';
$book = new Book($type, 'PHP for Unicorns','Rasmus Lerdorf');
writeln($book->getAuthorAndTitle());
$type = 'Stars';
$book = new Book($type, 'PHP for Ponys','Andi Gutmans');
writeln($book->getAuthorAndTitle());
function writeln($line_in) {
echo $line_in.PHP_EOL;
}
Update:
So if you are using an ORM we can assume that Book is your model class. I don't have any knowledge about Eloquent and how it handles data binding etc. so I'll make it as simple as I can. So I assume you can use a constructor with the binded data from database.
Keep your StrategyContext and the actual strategy classes -where your biz logic will be coded- as a service and use dependency injection while finding out which strategy you will use. This way you can bind all your strategies depending on your type variable, into your Model object.
Updated version of Book class,
class Book {
private $author = "Zeev Suraski";
private $title = "PHP for Cats";
private $strategy;
private $type = 'Caps';
function __construct() {
$this->strategy = new StrategyContext($this->type); //Dependency injection here
}
function getAuthor() {
return $this->strategy->showAuthor($this->author);
}
function getTitle() {
return $this->strategy->showTitle($this->title);
}
function getAuthorAndTitle() {
return $this->getTitle() . ' by ' . $this->getAuthor();
}
function setType($type) {
$this->type = $type;
}
function setStrategy($type=null) {
if($type==null) {
$this->strategy = new StrategyContext($this->type); //Dependency injection here
} else {
$this->strategy = new StrategyContext($type); //Dependency injection here
$this->setType($type);
}
}
}
writeln('BEGIN TESTING STRATEGY PATTERN');
writeln('');
$book = new Book();
writeln($book->getAuthorAndTitle());
$type = 'Exclaim';
$book->setType($type);
$book->setStrategy();
writeln($book->getAuthorAndTitle());
$type = 'Stars';
$book->setStrategy($type);
writeln($book->getAuthorAndTitle());
function writeln($line_in) {
echo $line_in.PHP_EOL;
}
I would use PHP's switch statement for this. For example,
switch($obj->type) {
case 1:
// do something
break;
case 2:
// do something else
break;
default:
// default actions
break;
}
Here break is used to stop execution of the switch statement as a whole. Without the break, code execution could fall through to the next case. This is sometimes desirable behavior though. If you wanted cases 1 and 2 to have the same code run, you could leave case 1 empty and write all your code in case 2. That way when the case 1 condition is met, no code related to case 1 would be run, but without a break statement, code execution would continue into case 2 before reaching the break statement in case 2.
I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.
I'm trying to build a form wizard in Kohana and am learning a bit as I go. One of the things that I've learn might work best is utilizing a state pattern in my class structure to manage the different steps a user can be in during the form process.
After doing some research, I've been thinking that the best approach may be to use an interface and have all of the steps act as states that implement the interface. After a state validates, it will change a session variable to the next step, which can be read upon the initial load of the interface and call the correct state to use.
Does this approach make sense? If so, how the heck do I make it happen (how do I best structure the filesystem?)
Here is the rough start I've been working on:
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Project_Builder #state
* Step_One #state
* Step_Two #state
**/
interface Project_Builder
{
public function do_this_first();
public function validate();
public function do_this_after();
}
class Step_One implements Project_Builder {
public function __construct
{
parent::__construct();
// Do validation and set a partial variable if valid
}
public function do_this_first()
{
echo 'First thing done';
// This should be used to set the session step variable, validate and add project data, and return the new view body.
$session->set('step', '2');
}
public function do_this_after()
{
throw new LogicException('Have to do the other thing first!');
}
}
class Step_Two implements Project_Builder {
public function do_this_first()
{
throw new LogicException('Already did this first!');
}
public function do_this_after()
{
echo 'Did this after the first!';
return $this;
}
}
class Project implements Project_Builder {
protected $state;
protected $user_step;
protected $project_data
public function __construct()
{
// Check the SESSION for a "step" entry. If it does not find one, it creates it, and sets it to "1".
$session = Session::instance('database');
if ( ! $session->get('step'))
{
$session->set('step', '1');
}
// Get the step that was requested by the client.
$this->user_step = $this->request->param('param1');
// Validate that the step is authorized by the session.
if ($session->get('step') !== $this->user_step)
{
throw new HTTP_Exception_404('You cannot skip a step!');
}
// Check if there is user data posted, and if so, clean it.
if (HTTP_Request::POST == $this->request->method())
{
foreach ($this->request->post() as $name => $value)
{
$this->project_data["$name"] = HTML::chars($value);
}
}
// Trigger the proper state to use based on the authorized session step (should I do this?)
$this->state = new Step_One;
}
public function doThisFirst()
{
$this->state = $this->state->do_this_first();
}
public function doThisAfter()
{
$this->state = $this->state->do_this_after();
}
}
$project = new Project;
try
{
$project->do_this_after(); //throws exception
}
catch(LogicException $e)
{
echo $e->getMessage();
}
$project = new Project;
$project->do_this_first();
$project->validate();
$project->do_this_after();
//$project->update();
Your way certainly looks possible, however I would be tempted to keep it simpler and use some of Kohanas build in features to take care of what you want. For example, I would use Kostache (mustache) and have separate View classes (and potentially templates) for each step. Then the controller becomes quite simple. See the example below (missing session stuff and validation of the step_number). All of the validation is handled in the model. If there is a validation error, an exception can be thrown which can then pass error messages back to the View.
<?php
class Wizard_Controller {
function action_step($step_number = 1)
{
$view = new View_Step('step_' + $step_number);
if ($_POST)
{
try
{
$model = new Model_Steps;
$model->step_number = $step_number;
if ($model->save($_POST))
{
// Go to the next step
$step_number++;
Request::current()->redirect('wizard/step/'.$step_number);
}
}
catch (Some_Kind_Of_Exception $e)
{
$view->post = $_POST;
$view->errors = $e->errors();
}
}
$view->render();
}
}
?>
Hope this makes sense.
I have created a File class, which takes care of all operations on files, I/O, and which acts differently depending on the nature of the files. I'm not happy with its actual structure, which looks like this:
class File
{
function __construct($id)
{
$bbnq = sprintf("
SELECT *
FROM documents
WHERE id = %u",
$id);
$req = bbnf_query($bbnq);
$bbn = $req->fetch();
$this->file_type = $bbn['file_type'];
$this->file_name = $bbn['file_name'];
$this->title = $bbn['title'];
}
function display()
{
return ''.$this->title.'';
}
}
class Image extends File
{
function __construct($id)
{
global $bbng_imagick;
if ( $bbng_imagick )
$this->imagick = true;
parent::__construct($id);
}
function display()
{
return '<img src="'.$this->file_name.'" alt="'.$this->title.'" />';
}
}
Here I need first to know the file type in order to determine which class/subclass to use.
And I'd like to achieve the opposite, i.e. send an ID to my class, which returns an object corresponding to the file type.
I have recently updated to PHP 5.3, and I know there are some new features which could be of use for creating a "factory" (late static bindings?). My OOP knowledge is pretty light, so I wonder if some have structural suggestions in order to make a unique class which will call the right constructor.
Thanks!
I don't think late static bindings is relevant here - a factory pattern doesn't require them. Try this:
class FileFactory
{
protected static function determineFileType($id)
{
// Replace these with your real file logic
$isImage = ($id>0 && $id%2);
$isFile = ($id>0 && !($id%2));
if ($isImage) return "Image";
elseif ($isFile) return "File";
throw new Exception("Unknown file type for #$id");
}
public static function getFile($id) {
$class = self::determineFileType($id);
return new $class($id);
}
}
// Examples usage(s)
for ($i=3; $i>=0; $i--) {
print_r(FileFactory::getFile($i));
}
As an aside, you should definitely escape your output from the DB, no matter how safe you think it is. Test with double quotes in a title, for example (let alone more malicious input).
Also if it's part of a project, you might want to separate the View layer (your HTML output) from this Model layer, ie implement MVC...
In your factory's constructor, you need to determine the file type, then with that, create an object of the corresponding class. Something like this perhaps:
class File
{
public static function factory($id)
{
$fileData = <query this $id>
switch ($fileData->type) {
case image:
return new ImageFile($fileData);
break;
case html:
return new HtmlFile($fileData);
break;
default:
// error?
}
}
}
abstract class FileAbstract
{
// common file methods here
}
// override the custom bits for each type
class ImageFile extends FileAbstract
{
public function display()
{
// ...
}
}
class HtmlFile extends FileAbstract
{
public function display()
{
// ...
}
}
Your code would then simply be:
$myFile = File::factory($id);
$myFile->display();