I am trying to make a singleton pattern but it seems I am making mistake somewhere. Everytime i try to create an instance from the class Cart it makes new one. Tested it with the setId() and getId() functions. It returns different number everytime.
class Cart
{
private $cartQuantity = 0;
private static $instance;
private $id;
private function __construct(){}
public function addQuantity($quantity){
$this->cartQuantity += $quantity;
}
public function getQuantity(){
return $this->cartQuantity;
}
public function setId(){
$this->id = rand(0, 10);
}
public function getId(){
return $this->id;
}
public static function startCount(){
if(self::$instance === null){
self::$instance = new Cart();
self::$instance->setId();
}
return self::$instance;
}
}
$inst = Cart::startCount();
echo $inst->getId();
What am I doing wrong? Seems like legit block of code to me :/ Thank you in advance!
Your singleton is working perfectly. I have tested it 10 times with the following code:
$inst = Cart::startCount();
echo $inst->getId();
$winst = Cart::startCount();
echo $inst->getId();
$ninst = Cart::startCount();
echo $ninst->getId();
$ninst->setId();
echo $ninst->getId();
echo $winst->getId();
$ginst = Cart::startCount();
echo $ginst->getId();
Results:
999444
555222
333999
333444
111111
222222
999999
888666
101010999
000777
If you change the ID, it keeps beeing changed. Otherwise always the same is returned. This is exactly what you should expect (note that rand(0,10)) also can deliver 10.
There is never more than one instance.
Related
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.
I have simplified my class for better understanding. Why is method2 giving me error that self::$dbconn is null? When remove __desctruc() it works fine?
I am calling it like so:
$test = new TestCtrl();
$test->getList(123);
...
class TestCtrl {
private static $dbconn;
function __construct(){
self::$dbconn = 'assume this is my db connection...';
}
function __destruct() {
self::$dbconn = null;
}
private function method1($contact){
$metas = self::method2();
return $metas;
}
private static function method2(){
$res = self::$dbconn;
return $res;
}
public function getList($contact){
return self::method1($contact);
}
}
Because you've declared $dbconn as static it is getting set to its original value between calls. Change it to a non-static variable and it should work fine.
I have a class with a bunch of chained methods. Here is an example:
class Sum {
public static $res = [];
private static $instance = null;
public static function run() {
if (self::$instance === null)
self::$instance = new self;
return self::$instance;
}
public function res() {
return self::$res;
}
public function addTen($int) {
self::$res = $this->addFour($str) + 6;
return $this;
}
public function addFour($int) {
self::$res = $int + 4;
return $this;
}
}
So if I want to call the addTen() method I can do like so:
echo Sum::run()->addFour(5)->res(); // Works, returns 9
echo Sum::run()->addTen(5)->res(); // Doesn't work
The above code doesn't work because the chained methods return the current object from the Sum class. So I managed to fix this by changing the addTen() method so it calls the res() method after the addFour() method like so:
public function addTen($int) {
self::$res = $this->addFour($str)->res() + 6;
return $this;
}
In the above case, that is ok because there is only on method being called from inside the addTen() method but what if I need to call a lot of other chained methods from inside the addTen() method? How can I do so the res() method is no longer needed to be called after every single call from another chained method inside the class (it could become unhandable to have a lot of "->res()" being called everywhere in the class).
I do not know what is your task for this class, but manually writing "add" per function will not make your class adaptable. As I have noted, you have used an array and not chain the $res properly. Since this is a sum class I would expect that you want to sum up the chain.
so I rewrote your class:
<?php
class Sum {
public static $res = [];
private static $instance = null;
public static function run() {
if (self::$instance === null)
self::$instance = new self;
return self::$instance;
}
public function res() {
return array_sum(self::$res);
}
public function add($int) {
self::$res[] = $int;
return $this;
}
}
$sum = new Sum();
$x = $sum->add(5)->add(6)->res();
echo $x; // 11
and you can see it work here:
https://3v4l.org/itDHN
I'm new to PHP OOP, but not so much to PHP, wanted to start learning.. and hit a few brick walls, but this one stumped me.. BUT since I can't find any questions anywhere on the web.. I can't find answers either..
So.. The Code.. I dumbed it down to its core problem, if I can understand that..
<?php
class wallet {
public $Money = 5;
public function Add($mMoney) {
$this->Money += $mMoney;
echo "added $mMoney to Wallet";
}
public function take($mMoney) {
$this->Money -= $nMoney;
}
public function check() {
echo $this->Money;
echo "Check?";
}
public function __get($var) {
echo "trying to get $var and Failing";
}
}
class person {
public $Name;
public $Wallet;
public $Purse;
public $Cash;
public function __construct($name, $cash) {
$this->Wallet = new wallet();
$this->Purse = new wallet();
$this->Name = $name;
$this->Cash = $cash;
}
public function status() {
echo "<br><table border = 1><tr><td>".$this->Name."</td><td> Wallet?</td><td> Purse </td></tr>
<tr><td> $ ".$this->Cash."</td><td>".$this->checkWallet()."<td>22</td></tr></table>";
}
public function toWallet($toAdd) {
$this->Wallet->add($toAdd);
}
public function checkWallet() {
echo $this->Wallet->check();
}
}
$bob = new person ("Bob", 10);
$sarah = new person ("Sarah", 20);
$bob->status();
$sarah->status();
$bob->toWallet(10);
$bob->status();
$sarah->status();
?>
Why won't toWallet Work? Why can't I access $this->checkWallet()..
The reason why ToWallet won't work is because the function thinks the echo from the Add is the return value. (Which is obviously a string and is useless in calculation). There needs to be a return command for the mMoney variable. Try typing something like this in the Add function
public function Add($mMoney) {
$this->Money += $mMoney;
echo "added $mMoney to Wallet";
return $mMoney;
}
I don't know how nobody else spotted that.
For Anyone who comes across this in the future.. Heres the Solution:
First: When you have an Object, don't echo or print the data to the screen, return it to the parent object to do that for you,
Second: Double check that all Variables are either function only i.e. $wallet, or are the objects variables $this->wallet because even though I was staring at the code for hours, I couldn't see that this was not the same variable..
new to OOP, what a mind-mess
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;