Example code for talk on refactoring with design patterns.
- https://joind.in/talk/view/10861
- Checkout Repository and use "DPC2014" branch for refactoring steps.
Example code for talk on refactoring with design patterns.
<?php | |
class ApplicationFactory | |
{ | |
private $solarium; | |
public function createSolariumClient() | |
{ | |
if ($this->solarium === null) { | |
$this->solarium = new SolariumClient(Config::getSolariumHost()); | |
} | |
return $this->solarium; | |
} | |
public function setSolariumClient($client) | |
{ | |
$this->solarium = $client; | |
} | |
public function createProductSearch() | |
{ | |
return new SolariumProductSearch( | |
$this->createSolariumClient() | |
); | |
} | |
} | |
class Config | |
{ | |
static public function getSolariumHost() | |
{ | |
return 'localhost:8080'; | |
} | |
} | |
class SolariumProductSearch | |
{ | |
private $solarium; | |
public function __construct($client) | |
{ | |
$this->solarium = $client; | |
} | |
public function queryForResults($req, $typeFilter, $tagsFilter) | |
{ | |
$solarium = $this->solarium; | |
$select = $solarium->createSelect(); | |
// filter by type | |
if ($typeFilter) { | |
$filterQueryTerm = sprintf('type:%s', $select->getHelper()->escapeTerm($typeFilter)); | |
$filterQuery = $select->createFilterQuery('type')->setQuery($filterQueryTerm); | |
$select->addFilterQuery($filterQuery); | |
} | |
// filter by tags | |
if ($tagsFilter) { | |
$tags = array(); | |
foreach ((array) $tagsFilter as $tag) { | |
$tags[] = $select->getHelper()->escapeTerm($tag); | |
} | |
$filterQueryTerm = sprintf('tags:(%s)', implode(' AND ', $tags)); | |
$filterQuery = $select->createFilterQuery('tags')->setQuery($filterQueryTerm); | |
$select->addFilterQuery($filterQuery); | |
} | |
if ($req->has('q')) { | |
$escapedQuery = $select->getHelper()->escapeTerm($req->get('q')); | |
$select->setQuery($escapedQuery); | |
} | |
$result = $solarium->query($select); | |
return $result; | |
} | |
} | |
class SearchController extends Controller | |
{ | |
private $solarium; | |
public function __construct($client, $productSearchService) | |
{ | |
$this->solarium = $client; | |
$this->productSearchService = $productSearchService; | |
} | |
/** | |
* @param Request $request | |
* | |
* @return Response | |
*/ | |
public function searchAction(Request $req) | |
{ | |
$typeFilter = $req->get('type'); | |
$tagsFilter = $req->get('tags'); | |
if ($req->has('q') || $typeFilter || $tagsFilter) { | |
$result = $this->productSearchService->queryForResults($req, $typeFilter, $tagsFilter); | |
if ($req->isXmlHttpRequest()) { | |
try { | |
return $this->render('ProductBundle:Search:list.html.twig', array( | |
'products' => $result, | |
'noLayout' => true, | |
)); | |
} catch (\Twig_Error_Runtime $e) { | |
if (!$e->getPrevious() instanceof \Solarium_Client_HttpException) { | |
throw $e; | |
} | |
return new JsonResponse(array( | |
'status' => 'error', | |
'message' => 'Could not connect to the search server', | |
), 500); | |
} | |
} else { | |
return $this->render('ProductBundle:Search:search.html.twig', array( | |
'products' => $result, | |
)); | |
} | |
} | |
return $this->render('ProductBundle:Search:search.html.twig', array( | |
'noLayout' => $req->isXmlHttpRequest(), | |
)); | |
} | |
} | |
abstract class Controller | |
{ | |
public function render($template, $variables) | |
{ | |
return new Response($template . json_encode($variables)); | |
} | |
public function generateUrl($route, $variables) | |
{ | |
if ($route == 'search') { | |
return '/search'; | |
} | |
return '/product/' . $variables['name']; | |
} | |
} | |
class Request | |
{ | |
private $variables = array(); | |
private $requestFormat = 'html'; | |
public function has($name) | |
{ | |
return isset($this->variables[$name]); | |
} | |
public function get($name) | |
{ | |
if (!$this->has($name)) { | |
return null; | |
} | |
return $this->variables[$name]; | |
} | |
public function set($name, $value) | |
{ | |
$this->variables[$name] = $value; | |
} | |
public function getRequestFormat() | |
{ | |
return $this->requestFormat; | |
} | |
public function setRequestFormat($format) | |
{ | |
$this->requestFormat = $format; | |
} | |
public function isXmlHttpRequest() | |
{ | |
return ($this->requestFormat === 'XmlHttpRequest'); | |
} | |
} | |
class Response | |
{ | |
public function __construct($content) | |
{ | |
$this->content = $content; | |
} | |
public function getContent() | |
{ | |
return $this->content; | |
} | |
} | |
class JsonResponse extends Response | |
{ | |
} | |
class SolariumClient | |
{ | |
public function createSelect() | |
{ | |
return new SolariumSelect(); | |
} | |
public function query($select) | |
{ | |
return new ArrayIterator(array( | |
new Product('foo', 'A foo product', 42), | |
new Product('bar', 'A bar product', 23), | |
)); | |
} | |
} | |
class SolariumSelect | |
{ | |
public function __call($method, $args) | |
{ | |
return $this; | |
} | |
} | |
class Product | |
{ | |
public $name; | |
public $description; | |
public $price; | |
public function __construct($name, $description, $price) | |
{ | |
$this->name = $name; | |
$this->description = $description; | |
$this->price = $price; | |
} | |
} |
<?php | |
require_once __DIR__ . '/Refactoring.php'; | |
class SearchControllerTest extends \PHPUnit_Framework_TestCase | |
{ | |
public function testController() | |
{ | |
$ctrl = $this->createSearchController(); | |
$response = $ctrl->searchAction(new Request()); | |
$this->assertEquals( | |
"ProductBundle:Search:search.html.twig{\"noLayout\":false}", | |
$response->getContent() | |
); | |
} | |
public function testControllerWithSearch() | |
{ | |
$request = new Request(); | |
$request->set('q', 'Hello'); | |
$ctrl = $this->createSearchController(); | |
$response = $ctrl->searchAction($request); | |
$this->assertEquals( | |
'ProductBundle:Search:search.html.twig{"products":{"0":{"name":"foo","description":"A foo product","price":42},"1":{"name":"bar","description":"A bar product","price":23}}}', | |
$response->getContent() | |
); | |
} | |
public function testControllerWithXmlHttpRequest() | |
{ | |
$request = new Request(); | |
$request->set('q', 'Hello'); | |
$request->setRequestFormat('XmlHttpRequest'); | |
$ctrl = $this->createSearchController(); | |
$response = $ctrl->searchAction($request); | |
$this->assertEquals( | |
'ProductBundle:Search:list.html.twig{"products":{"0":{"name":"foo","description":"A foo product","price":42},"1":{"name":"bar","description":"A bar product","price":23}},"noLayout":true}', | |
$response->getContent() | |
); | |
} | |
private function createSearchController() | |
{ | |
$factory = new ApplicationFactory(); | |
$client = $factory->createSolariumClient(); | |
$ctrl = new SearchController($client, $factory->createProductSearch()); | |
return $ctrl; | |
} | |
} | |