需求描述
胖仔,我要一個購物車功能用來買系列書用的,例如說哈利波特全套,但是我要有個按了系列選擇後把所有書都放到購物車內,這有沒有辦法做到?喔對了順帶一提,這個回傳的資料有可能之後需要因為第三方要接會修改喔,記得寫的“彈性”一點,還有後台也要能看到系列書的列表。
$ composer create-project slim/slim-skeleton slim-skeleton
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`series` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
LOCK TABLES `products` WRITE;
/*!40000 ALTER TABLE `products` DISABLE KEYS */;
INSERT INTO `products` (`id`, `series`, `name`, `price`)
VALUES
(1,'哈利波特','神秘的魔法石',500),
(2,'哈利波特','消失的密室',400),
(3,'哈利波特','阿茲卡班的逃犯',450),
(4,'哈利波特','火盃的考驗',450),
(5,'哈利波特','鳳凰會的密令',650),
(6,'哈利波特','混血王子的背叛',550),
(7,'哈利波特','死神的聖物',600);
/*!40000 ALTER TABLE `products` ENABLE KEYS */;
UNLOCK TABLES;
他要有個接口可以打,沒有牽扯到寫入問題所以可以用 GET 的方式做
<?php
//src/routes.php
$app->get('/cart/items/[{series}]', function ($request, $response, $args) {
//do something.
});
$ Generating autoload files
<?php
// controllers
$container[App\Controllers\ProductController::class] = function($container) {
return new App\Controllers\ProductController();
};
<?php
//routes.php
$app->get('/cart/items/[{series}]', App\Controllers\ProductController::class);
public function __invoke()
{
echo 1234;
}
需要返回指定的格式跟系列書
要有 database eloquent
$ composer require illuminate/database
// Slim Settings
'determineRouteBeforeAppMiddleware' => false,
'db' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]
<?php
// Service factory for the ORM
$container['db'] = function ($container) {
$capsule = new \Illuminate\Database\Capsule\Manager;
$capsule->addConnection($container['settings']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
return $capsule;
};
<?php
// controllers
$container[App\Controllers\ProductController::class] = function ($container) {
$db = $container->get('db');
return new App\Controllers\ProductController($db);
};
<?php
namespace App\Controllers;
use Illuminate\Database\Capsule\Manager;
class ProductController
{
/**
* @var Manager
*/
private $db;
public function __construct(Manager $db)
{
$this->db = $db;
}
public function __invoke()
{
$products = $this->db->table('products')->get();
dd($products);
}
}
<?php
$app->get('/cart/items/[{series}]', App\Controllers\ProductController::class . ':series');
public function series(RequestInterface $request, ResponseInterface $response, $args)
{
$products = $this->db->table('products')->where('series', $args['series'])->get();
return $response->withJson($products->toArray());
}
相依於抽象不是實體
/cart/items/哈利波特
這個指定的格式也許之後會修改
transform pattern
新建一個 contract 來實現 transform
interface Transformable
{
public function transform($attributes);
}
<?php
namespace App\Transformers;
use App\Contracts\Transformable;
class ProductTransformer implements Transformable
{
public function transform($attributes)
{
// TODO: Implement transform() method.
}
}
// transformer
$container[App\Transformers\ProductTransformer::class] = function($container) {
return new App\Transformers\ProductTransformer;
};
// controllers
$container[App\Controllers\ProductController::class] = function ($container) {
$db = $container->get('db');
$transform = $container->get(App\Transformers\ProductTransformer::class);
return new App\Controllers\ProductController($db, $transform);
};
class ProductController
{
/**
* @var Manager
*/
private $db;
/**
* @var Transformable
*/
private $transformer;
public function __construct(Manager $db, Transformable $transformer)
{
$this->db = $db;
$this->transformer = $transformer;
}
public function series(RequestInterface $request, ResponseInterface $response, $args)
{
$products = $this->db->table('products')->where('series', $args['series'])->get();
return $response->withJson($this->transformer->transform($products));
}
public function transform($attributes)
{
$result[] = $attributes->map(function ($product) {
return [
'name' => $product->name,
'price' => $product->price,
];
});
return $result;
}
還記得剛剛說過"每個類只能有一種理由改變
"嗎?
當你要改變 query 條件時卻改變了 controller 這就不太合理了,controller 不就是負責呼叫和 render or response?
<?php
namespace App\Repositories;
class ProductRepository
{
}
//dependencies
// repositories
$container[App\Repositories\ProductRepository::class] = function($container) {
return new App\Repositories\ProductRepository($container);
};
這邊注意我是 inject container
use Slim\Container;
class ProductRepository
{
protected $builder;
/**
* @var Container
*/
private $container;
public function __construct(Container $container)
{
$this->container = $container;
$this->newQueryBuilder();
}
public function newQueryBuilder()
{
$this->builder = $this->container->get('db');
return $this;
}
}
// controllers
$container[App\Controllers\ProductController::class] = function ($container) {
$repository = $container->get(App\Repositories\ProductRepository::class);
$transform = $container->get(App\Transformers\ProductTransformer::class);
return new App\Controllers\ProductController($repository, $transform);
};
class ProductController
{
/**
* @var ProductRepository
*/
private $repository;
/**
* @var Transformable
*/
private $transformer;
public function __construct(ProductRepository $repository, Transformable $transformer)
{
$this->repository = $repository;
$this->transformer = $transformer;
}
public function getBySeries($series)
{
return Product::where('series', $series)->get();
}
public function scopeSeries($query, $value)
{
return $query->where('series', $value);
}
public function getBySeries($series)
{
return Product::series($series)->get();
}
class ProductRepository extends Repository
{
public function __construct($container)
{
parent::__construct($container);
}
public function getBySeries($series)
{
return Product::series($series)->get();
}
}
abstract class Repository
{
protected $builder;
/**
* @var Container
*/
private $container;
public function __construct(Container $container)
{
$this->container = $container;
$this->newQueryBuilder();
}
public function newQueryBuilder()
{
$this->builder = $this->container->get('db');
return $this;
}
}
public function transform($attributes)
{
$result = $attributes->map(function ($product) {
return [
'name' => $product->name,
'price' => $product->price,
];
});
$result = array_add($result, 'total', $attributes->sum('price'));
return $result;
}
public function scopeSeries($query, $value)
{
return $query->where('series', 'like', "%{$value}%");
}
use Prophecy\Exception\Doubler\ClassNotFoundException;
abstract class Controller
{
public function transform($data)
{
$class = str_replace('Controller', 'Transformer', static::class);
if (! class_exists($class)) {
throw new ClassNotFoundException('Class ' . $class .' does NOT exist!', $class);
}
$transformer = new $class;
return $transformer->transform($data);
}
}
// controllers
$container[App\Controllers\ProductController::class] = function ($container) {
$repository = $container->get(App\Repositories\ProductRepository::class);
return new App\Controllers\ProductController($repository);
};