Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When the parameters within the construct of the Command class are many #11

Open
mdeprezzo opened this issue Jul 26, 2014 · 15 comments
Open

Comments

@mdeprezzo
Copy link

Hi guys,
this is a not real issue, but i want to tell you what i mean.
Sometimes we need to process a lot of data, and not only a few like in the larabook's lesson.
So in this situation the mapInputToCommand method doesn't works like we expect, if we think that we have an array like params for the construct, similar to something like that:

    public function __construct(array $attributes)
    {
        $this->attributes = $attributes;
    }

what can we do ? I must simply ignore the mapInput method ? Or is a mistake think like that ?

@ratiw
Copy link

ratiw commented Jul 27, 2014

I'm having quite the same issue, for example the CreatePurchaseOrderCommand class, where there are a lot of properties and it doesn't really make sense to throw everything in the constructor.

So, what I did was modifying the mapInputToCommand method to also look into the public properties of the class when the constructor is not available. So now for the class which has a lot of properties, I just define those properties as public and not define the constructor for that class.

Here is the code:

    // replace this code in the CommanderTrait.php

    protected function mapInputToCommand($command, array $input)
    {
        $dependencies = [];

        $class = new ReflectionClass($command);

        $parameters = ($hasConstructor = !is_null($class->getConstructor()))
            ? $class->getConstructor()->getParameters()
            : $class->getProperties(\ReflectionProperty::IS_PUBLIC);

        foreach ($parameters as $parameter)
        {
            $name = $parameter->getName();

            if (array_key_exists($name, $input))
            {
                $dependencies[] = $input[$name];
            }
            elseif ($hasConstructor && $parameter->isDefaultValueAvailable())
            {
                $dependencies[] = $parameter->getDefaultValue();
            }
            else
            {
                throw new InvalidArgumentException("Unable to map input to command: {$name}");
            }
        }

        return $class->newInstanceArgs($dependencies);
    }

@mdeprezzo
Copy link
Author

Oh i understood, i think that can be a good resolution in that case. I'll try it.

@ratiw
Copy link

ratiw commented Jul 28, 2014

@Mdpproduction Sorry, my initial solution does not work because I overlook 'newIntanceArgs()'. Please see PR#14 instead, it should work now.

@mdeprezzo
Copy link
Author

@ratiw Ya, sorry dude, i forgot to warn you about that, anyway i fixed it myself with something similar like your code. Yesterday night i did tried another change inside that method, maybe isn't in line with dto pattern but works like map by type.

@IsraelOrtuno
Copy link

I still find this a little bit repetitive. Just wondering if you add 3 new fields to your "Orders" table... Would you have to check your $fillable attribute in your model, also check your command bus? This is the only thing annoying me about using this package.

@mdeprezzo
Copy link
Author

@IsraelOrtuno You're right about this. But maybe this is the way it works the DTO, i know is a bit annoying if you have a lot of properties, to set inside the command Class. Maybe we can ask to Jeffrey more info for this particular case.

@IsraelOrtuno
Copy link

@Mdpproduction What I've done so far... Created a $attributes property into an abstract command that every command extends from, modified the validator to validate $command->attributes array and the mapInputToCommand just sets that attribute property to the input.

@ratiw
Copy link

ratiw commented Jul 29, 2014

@IsraelOrtuno I was thinking about the same thing you mentioned after reading through the discussion on another PR#15 regarding the SRP violations. I like the idea of the command being able to validate before it gets created, so only valid command gets pass into the command bus.

@iamohd-zz
Copy link

faced the same issue, anyone managed to get a good solution?

@ratiw
Copy link

ratiw commented Aug 4, 2014

What do you think about my solution?
https://github.com/ratiw/Commander/blob/196555f354d4e4df13a8f60a2627aa6992e768c0/src/Laracasts/Commander/CommanderTrait.php

Any suggestion and comment are very welcome as I can also learn on this. :)

@bruno-barros
Copy link

I did send a PR. Check if is worthy to you. :-)

@bruno-barros
Copy link

@ratiw I found a problem to this aproach. When writing tests I'm instantiating the CommandHandler and passing the Command, so without the constructor I can't do it.
On my case this solution is handy when I'm updating a entity, because I can pass just the attributes I need to update. My solution, for now, is pass the ID and a DATA array with attributes I need to update.

@ratiw
Copy link

ratiw commented Aug 14, 2014

@bruno-barros I don't quite understand what you mean. Can you please share your test code so that I can also have a look and learn from it?

@bruno-barros
Copy link

Of course!

class CreateAdminCommandTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();
        $this->handler = App::make('Namespace\CreateAdminCommandHandler');      

    }

    public function test_something()
    {
        $this->handler->handle(new CreateAdminCommand('name', 'email'));
    }
}

So, to test the behavior with diferent inputs I'm instatiating the command like this. If the command has no constructor I could not pass the data. The TestCase has no access to CommandBusTrait to run the $this->execute() method.

@ratiw
Copy link

ratiw commented Aug 15, 2014

@bruno-barros I think you could do something like this.

public function test_something()
{
    $command = new CreateAdminCommand;
    $command->name = 'Some Name';
    $command->email = '[email protected]';
    $this->handler->handle($command);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants