Consistent implementation functions in a logical order - php

Maybe I'm wrong to expressed it in the title, but I just do not understand how in the class like this.
<?php
class sample{
public $data = [];
public function pushIndex($index){
array_push($this->data, $index);
}
public function pushValue($value){
array_push($this->data["index"], $value);
// Some magic
}
public function forIndex($index){
return $this->data[$index];
// Some magic
}
}
To realize scheme like in Symfony, where will be spaghetti like this
<?php
$a = new sample;
$a->pushIndex("index")->pushValue("value");
$a->forIndex("index2")->pushValue("value2");
Maybe someone knows how to do it?

What you're talking about is called Fluent interface.
Returns the current object by using $this.
public function pushIndex($index){
array_push($this->a,$index);
return $this;
}
But what you want is to do something like this:
class sample
{
protected $a = [];
protected $currentIndex = null;
public function pushIndex($index)
{
$this->currentIndex = $index;
return $this;
}
public function pushValue($value)
{
if ($this->currentIndex === null) {
throw new LogicException('You need to call "pushIndex" or "forIndex" first.');
}
$this->a[$this->currentIndex] = $value;
return $this;
}
public function forIndex($index)
{
if (!isset($this->a[$index])) {
throw new RuntimeException(sprintf('Index "%s" doesn\'t exists', $index));
}
$this->currentIndex = $index;
return $this;
}
public function getArray()
{
return $this->a;
}
}
$a = new sample;
$a->pushIndex("index")->pushValue("value");
$a->forIndex("index2")->pushValue("value2"); // exception?
var_dump($a->getArray());
But what you want is pretty unclear.

I think what you're trying to achieve is something like this:
class sample{
public $a = [];
public $index = null;
public function pushIndex($index){
$this->index = $index;
$this->a[$index] = null;
return $this;
}
public function pushValue($value){
$this->a[$this->index] = $value;
return $this;
}
public function forIndex($index){
$this->index = $index;
return $this;
}
}
$a = new sample;
$a->pushIndex("index")->pushValue("value");
$a->forIndex("index2")->pushValue("value2");
echo "<pre>";
var_dump($a);
echo "</pre>";
This is called "method chaining". By returning a reference to the called object, you're able to perform further methods on the object, essentially "chaining" the methods.
I've had to adjust your code a little to get it the work I believe the way you want it to. It should provide a working example to help you understand method chaining.

Related

How to load data with new self construction php

I can not load data to properties using this construction I receive null in dump
<?php
namespace App\Domain\Good;
class GoodDto
{
public $name;
public $articul;
public $price;
public $type;
public $qnt;
public $discount;
public $category;
public $description;
public $description2;
public $color;
public function load($data)
{
$this->name = $data['name'];
$this->articul = $data['artikul'];
$this->price = $data['price'];
$this->type = (isset($data['type'])) ? $data['type'] : null;
$this->qnt = $data['count'];
$this->discount = $data['spinner-decimal'];
$this->category = $data['id_cat'];
$this->description = $data['editor1'];
$this->description2 = '';
$this->color = $data['color'];
//$this->user_id = Auth::user()->id;
}
public static function fromRequest($request)
{
dump('inp=>',(new self ())->load($request->input()));
return (new self ())->load($request->input());
}
}
Please explain to me why I receive null while request->input() is an array, I call it from another place
$dto=GoodDto::fromRequest($request);
Method chaining, returns the last return from the chain. The other returns are used to call the next link in the chain.
(new self ())->load()
So load() needs to return $this
public function load($data)
{
...
return $this;
}
Currently it returns null, which is why it returns null.
See you are not saving the instance from the constructor, instead you pass it to load by enclosing it within the (....). By pass it I mean you call the load method on the return from the constructor.
You can test this like so:
class foo{
function load(){
return $this;//return this
}
}
var_dump((new foo)->load());
class bar{
function load(){
//return null
}
}
var_dump((new bar)->load());
Output
//return this
object(foo)#1 (0) {
}
//return null
NULL
sandbox
The second class in the example above class bar, is essentially what you are doing.
PS. forgot to scroll down on your post at first ... lol ... So I had to update my answer.
Bonus
You can also simplify the load code like this:
public function load($data)
{
foreach($data as $prop=>$value){
if(property_exists($this,$prop)) $this->$prop = $value;
}
return $this;
}
This way if you add new properties you don't have to edit the load method ever again, you just have to name the array elements the same as the class properties. You can even throw an error if the property does not exist if you want, by adding an else to the condition etc...
Personally, when I do this I prefer to call a set method like this:
//eg. $data = ['foo' => '2019-06-16']
public function load(array $data)
{
foreach($data as $prop=>$value){
$method = 'set'.$prop; //$method = 'setfoo' using the example above
if(method_exists($this,$method )){
$this->$method($value); //calls 'setfoo' with '2019-06-16'
}else{
throw new Exception('Unknown method '.$method);
}
}
return $this;
}
public function setFoo($date){
$this->foo = new DateTime($date);
}
Then you can apply some transforms to the data etc... PHP method names are not case sensitive. You can even combine these by first checking for a method then a property then throw the error etc...
Cheers.

Method chaining a get function to return specific $this properties

I want to be able to use an object like below, to retrieve new orders and new invoices. I feel like it is most readable, but I am having trouble writing the PHP class to work this way.
$amazon = new Amazon();
$amazon->orders('New')->get();
$amazon->invoices('New')->get();
In my PHP class, how would my get() method be able to distinguish whether to return orders or invoices?
<?php
namespace App\Vendors;
class Amazon
{
private $api_key;
public $orders;
public $invoices;
public function __construct()
{
$this->api_key = config('api.key.amazon');
}
public function orders($status = null)
{
$this->orders = 'orders123';
return $this;
}
public function invoices($status = null)
{
$this->invoices = 'invoices123';
return $this;
}
public function get()
{
// what is the best way to return order or invoice property
// when method is chained?
}
}
A couple of ways, if you want it dynamic and don't do any logic in the methods, use something like __call
<?php
class Amazon {
public $type;
public $method;
public function get()
{
// do logic
// ...
return 'Fetching: '.$this->method.' ['.$this->type.']';
}
public function __call($method, $type)
{
$this->method = $method;
$this->type = $type[0];
return $this;
}
}
$amazon = new Amazon();
echo $amazon->orders('New')->get();
echo $amazon->invoices('New')->get();
If you want to do logic in the methods, do something like:
<?php
class Amazon {
public $type;
public $method;
public function get()
{
return 'Fetching: '.$this->method.' ['.$this->type.']';
}
public function orders($type)
{
$this->method = 'orders';
$this->type = $type;
// do logic
// ...
return $this;
}
public function invoices($type)
{
$this->method = 'invoices';
$this->type = $type;
// do logic
// ...
return $this;
}
}
$amazon = new Amazon();
echo $amazon->orders('New')->get();
echo $amazon->invoices('New')->get();
As orders and invoices are set methods, I would suggest to do as follows:
public function get(array $elements)
{
$result = [];
foreach($elements as $element) {
$result[$element] = $this->$element;
}
return $result;
}
So, you can call get method as:
$amazon = new Amazon();
$amazon->orders('New')->invoices('New')->get(['orders', 'invoices']);
** You need to validate the element's availability within the get method.

Cloning $this to do chaining. Is it a bad idea?

I am having the following class:
class StuffDoer{
public function __construct(Dep1 $dep, Dep2 $dep2, array $array){
$this->dep = $dep;
$this->dep2 = $dep2;
$this->array = $array;
}
public function genericDoStuff($param){
// Do stuff here...
}
public function doStuffForMark(){
return $this->genericDoStuff('Mark');
}
public function doStuffForTim(){
return $this->genericDoStuff('Tim');
}
public function doStuffForAlice(){
return $this->genericDoStuff('Alice');
}
}
After some months, I am asked to make the method genericDoStuff($param), along with all the methods that depend on it, use an extra parameter in a single part of the application. Instead of changing the signature on every single method that depends on genericDoStuff, I ended up with the following:
class StuffDoer{
public function __construct(Dep1 $dep, Dep2 $dep2, array $array){
$this->dep = $dep;
$this->dep2 = $dep2;
$this->array = $array;
}
public function forParameter($param){
$self = clone $this;
$this->param = $param;
return $self;
}
public function genericDoStuff($param){
if($this->param !== null){
// Do stuff by taking param into account
} else {
// Do stuff stuffdoer does
}
}
public function doStuffForMark(){
return $this->genericDoStuff('Mark');
}
public function doStuffForTim(){
return $this->genericDoStuff('Tim');
}
public function doStuffForAlice(){
return $this->genericDoStuff('Alice');
}
}
That way, I am able to do this in the single point of the application:
$myStuffDoer = $serviceContainer->get('stuff_doer');
$myStuffDoer->forParameter('AAAARGHITBURNSGODHELPME')->doStuffForMark();
// Future usages of $myStuffDoer are unaffected by this!
So my question is this: Is this considered a bad practice for any reason?

How can I implement Method Chaining in PHP 5.x?

I have the following class written for PHP 5.4.x. Should this work as I expect?
class SqlBuilder {
private $dbTable;
private $action;
private $data;
private $clause;
public function toString() {
// $sql = generate sql string
// [...]
return $sql;
}
[...]
public function setClause($clause) {
$this->clause = $clause;
}
public function setDbTable($dbTable) {
$this->dbTable = $dbTable;
}
public function setAction($action) {
$this->action = $action;
}
}
$sql = (new \dbal\SqlBuilder())
->setAction($this->action)
->setClause($this->clause)
->setDbTable($this->dbTable)
->toString();
I am expecting to be able to access all of my setter methods. Instead I see the following error:
Fatal error: Call to a member function toString() on a non-object )
This seems to work:
$builder= new \dbal\SqlBuilder();
$builder->setAction($this->action)
$builder->setClause($this->clause)
$builder->setDbTable($this->dbTable)
$sql = $builder->toString();
But I know that this works as well:
class Foo
{
public $a = "I'm a!";
public $b = "I'm b!";
public $c;
public function getB() {
return $this->b;
}
public function setC($c) {
$this->c = $c;
return $this;
}
public function getC() {
return $this->c;
}
}
print (new Foo)
->setC($_GET["c"])
->getC(); // I'm c!
I've used this style of syntax in Javascript before. Is there a way to make it work in PHP?
What you are asking about is called method chaining. In order for it to work the way you want, each method call needs to return a reference to the object that you are calling. So,
->setAction($this->action)
// needs to return $this; so that
->setClause($this->clause)
// knows what to operate upon and in turn needs to return $this; so that
->setDbTable($this->dbTable)
// can do the same
Try :
public function setClause($clause) {
$this->clause = $clause;
return $this;
}
public function setDbTable($dbTable) {
$this->dbTable = $dbTable;
return $this;
}
public function setAction($action) {
$this->action = $action;
return $this;
}

How this class and sub methods use works?

I have been browsing some php source code and need to know how the following class and sub methods use works:
<?php
$me = new Person;
$me->name("Franky")->surname("Chanyau")->phone("+22", "456 789");
?>
I have pretty solid knowledge of OOP so I don't want a 101. I just need to know how to make the above code possible.
Method chaining is possible, by
return $this;
at the end of the method.
Explained here:
phpandstuff: Method Chaining Plus Magic Setters
These methods usually set an instance variable and then just return $this.
public function phone($param) {
$this->phone = $param;
return $this;
}
methods name() surname() and phone() return an instance of Person. you can accomplish this by
return $this;
most probably these methods look like this:
public function name($name) {
$this->name = $name;
return $this;
}
like some others said, its a fluid interface http://en.wikipedia.org/wiki/Fluent_interface#PHP the Basic Idea is that a methof of a class always returns the object itself
class Car {
private $speed;
private $color;
private $doors;
public function setSpeed($speed){
$this->speed = $speed;
return $this;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function setDoors($doors) {
$this->doors = $doors;
return $this;
}
}
// Fluent interface
$myCar = new Car();
$myCar->setSpeed(100)->setColor('blue')->setDoors(5);
(via wiki)
It's called method chaining. Basically each class function returns the object itself ($this) so that the user can call more functions on the returned object.
public function name() {
//other stuff...
return $this;
}
http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html
http://www.electrictoolbox.com/php-method-chaining
The idea is if we return $this then we can chain the object method calls together. Here's the solution:
<?php
class Person
{
private $strName;
private $strSurname;
private $ArrPhone = array();
public function name($strName)
{
$this->strName = $strName;
return $this; // returns $this i.e Person
}
public function surname($strSurname)
{
$this->strSurname = $strSurname;
return $this; // returns $this i.e Person
}
public function phone()
{ $this->ArrPhone = func_get_args(); //get arguments as array
return $this; // returns $this i.e Person
}
public function __toString()
{
return $this->strName." ".$this->strSurname.", ".implode(" ",$this->ArrPhone);
}
}
$me = new Person;
echo $me->name("Franky")->surname("Chanyau")->phone("+22", "456 789");
?>
Correct answers, but to make the code work you should write:
$me = new Person();
instead of
$me = new Person;

Categories