Item implements ArrayAccess 這個 interface 後
Item 物件就像是在操作 Array
但事實上只完成了一半的工作,
所以今天就再來重構一下 Item 的物件
讓它可以使用的更靈活一些
接下來的目標是不變更舊有單元測試進行修改
新功能則增加新的單元測試
// tests/ItemTest.php
namespace Recca0120\Cart\Tests;
use Recca0120\Cart\Item;
use PHPUnit\Framework\TestCase;
class ItemTest extends TestCase
{
/** @test */
public function 測試商品屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item($attributes);
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 使用mehtod來設定屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item();
$item->setName($attributes['name']);
$this->assertSame($attributes['name'], $item->getName());
$item->setPrice($attributes['price']);
$this->assertSame($attributes['price'], $item->getPrice());
$item->setQuantity($attributes['quantity']);
$this->assertSame($attributes['quantity'], $item->getQuantity());
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 測試ArrayAccess()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item();
$item['name'] = $attributes['name'];
$this->assertSame($attributes['name'], $item['name']);
$item['price'] = $attributes['price'];
$this->assertSame($attributes['price'], $item['price']);
$item['quantity'] = $attributes['quantity'];
$this->assertSame($attributes['quantity'], $attributes['quantity']);
$this->assertEquals($attributes, $item->toArray());
}
}
原始 Item.php
// src/Item.php
<?php
namespace Recca0120\Cart;
use ArrayAccess;
class Item implements ArrayAccess
{
private $attributes = [];
public function __construct($attributes = [])
{
$this->attributes = $attributes;
}
public function setName($name)
{
$this->attributes['name'] = $name;
return $this;
}
public function getName()
{
return $this->attributes['name'];
}
public function setPrice($price)
{
$this->attributes['price'] = $price;
return $this;
}
public function getPrice()
{
return $this->attributes['price'];
}
public function setQuantity($quantity)
{
$this->attributes['quantity'] = $quantity;
return $this;
}
public function getQuantity()
{
return $this->attributes['quantity'];
}
public function toArray()
{
return $this->attributes;
}
public function offsetExists($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
return isset($this->attributes[$offset]);
}
public function offsetGet($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
return $this->attributes[$offset];
}
public function offsetSet($offset, $value)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
$this->attributes[$offset] = $value;
}
public function offsetUnset($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
unset($this->attributes[$offset]);
}
}
利用 __call 的 magic method 來將所有的 setXXX, getXXX 取代掉
namespace Recca0120\Cart;
use ArrayAccess;
use RuntimeException;
class Item implements ArrayAccess
{
private $attributes = [];
public function __construct($attributes = [])
{
$this->attributes = $attributes;
}
public function __call($method, $paramters)
{
if (strpos($method, 'set') === 0) {
$this->attributes[lcfirst(substr($method, 3))] = $paramters[0];
return $this;
}
if (strpos($method, 'get') === 0) {
return $this->attributes[lcfirst(substr($method, 3))];
}
throw new RuntimeException('Call to undefined method '.__CLASS__.'::'.$method);
}
public function toArray()
{
return $this->attributes;
}
public function offsetExists($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
return isset($this->attributes[$offset]);
}
public function offsetGet($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
return $this->attributes[$offset];
}
public function offsetSet($offset, $value)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
$this->attributes[$offset] = $value;
}
public function offsetUnset($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
unset($this->attributes[$offset]);
}
}
修改到這邊記得執行一次 phpunit 來確保程式無誤
接下來就再利用 __get, __set. __isset 這兩個 magic method
讓 Item 能直接存取 $attributes 的屬性內容
// tests/ItemTest.php
<?php
namespace Recca0120\Cart\Tests;
use Recca0120\Cart\Item;
use PHPUnit\Framework\TestCase;
class ItemTest extends TestCase
{
/** @test */
public function 測試商品屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item($attributes);
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 使用mehtod來設定屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item();
$item->setName($attributes['name']);
$this->assertSame($attributes['name'], $item->getName());
$item->setPrice($attributes['price']);
$this->assertSame($attributes['price'], $item->getPrice());
$item->setQuantity($attributes['quantity']);
$this->assertSame($attributes['quantity'], $item->getQuantity());
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 測試ArrayAccess()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item();
$item['name'] = $attributes['name'];
$this->assertSame($attributes['name'], $item['name']);
$item['price'] = $attributes['price'];
$this->assertSame($attributes['price'], $item['price']);
$item['quantity'] = $attributes['quantity'];
$this->assertSame($attributes['quantity'], $attributes['quantity']);
$this->assertEquals($attributes, $item->toArray());
}
/** @test */
public function 取得商品屬性()
{
$attributes = [
'name' => '商品01',
'price' => 100,
'quantity' => 2,
];
$item = new Item;
$item['name'] = $attributes['name'];
$item->price = $attributes['price'];
$item->setQuantity($attributes['quantity']);
$this->assertEquals($attributes, $item->toArray());
}
}
// src/Item.php
namespace Recca0120\Cart;
use ArrayAccess;
use RuntimeException;
class Item implements ArrayAccess
{
private $attributes = [];
public function __construct($attributes = [])
{
$this->attributes = $attributes;
}
public function __get($key)
{
return $this->attributes[$key];
}
public function __set($key, $value)
{
return $this->attributes[$key] = $value;
}
public function __isset($key)
{
return isset($this->attributes[$key]);
}
public function __unset($key)
{
unset($this->attributes[$key]);
}
public function __call($method, $paramters)
{
if (strpos($method, 'set') === 0) {
$this->attributes[lcfirst(substr($method, 3))] = $paramters[0];
return $this;
}
if (strpos($method, 'get') === 0) {
return $this->attributes[lcfirst(substr($method, 3))];
}
throw new RuntimeException('Call to undefined method '.__CLASS__.'::'.$method);
}
public function toArray()
{
return $this->attributes;
}
public function offsetExists($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
return isset($this->attributes[$offset]);
}
public function offsetGet($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
return $this->attributes[$offset];
}
public function offsetSet($offset, $value)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
$this->attributes[$offset] = $value;
}
public function offsetUnset($offset)
{
$offset = $offset === 'qty' ? 'quantity' : $offset;
unset($this->attributes[$offset]);
}
}
這樣 Item 這個物件的靈活性就變的非常高了
不過寫到這邊會發現有太多的重覆程式碼
所以可以再一次重構
// src/Item.php
namespace Recca0120\Cart;
use ArrayAccess;
use RuntimeException;
class Item implements ArrayAccess
{
private $attributes = [];
public function __construct($attributes = [])
{
$this->attributes = $attributes;
}
public function __get($key)
{
$key = $key === 'qty' ? 'quantity' : $key;
return $this->attributes[$key];
}
public function __set($key, $value)
{
$key = $key === 'qty' ? 'quantity' : $key;
$this->attributes[$key] = $value;
return $this;
}
public function __isset($key)
{
$key = $key === 'qty' ? 'quantity' : $key;
return isset($this->attributes[$key]);
}
public function __unset($key)
{
$key = $key === 'qty' ? 'quantity' : $key;
unset($this->attributes[$key]);
}
public function __call($method, $paramters)
{
if (strpos($method, 'set') === 0) {
return $this->__set(lcfirst(substr($method, 3)), $paramters[0]);
}
if (strpos($method, 'get') === 0) {
return $this->__get(lcfirst(substr($method, 3)));
}
throw new RuntimeException('Call to undefined method '.__CLASS__.'::'.$method);
}
public function offsetGet($key)
{
return $this->__get($key);
}
public function offsetSet($key, $value)
{
return $this->__set($key, $value);
}
public function offsetExists($key)
{
return $this->__isset($key);
}
public function offsetUnset($key)
{
$this->__unset($key);
}
public function toArray()
{
return $this->attributes;
}
}
今天有點累就先優化到這邊,明天再繼續