I'm implementing a search functionality and based on the query parameter i use a different class to search.
class Search {
public function getResults()
{
if (request('type') == 'thread') {
$results = app(SearchThreads::class)->query();
} elseif (request('type') == 'profile_post') {
$results = app(SearchProfilePosts::class)->query();
} elseif (request()->missing('type')) {
$results = app(SearchAllPosts::class)->query();
}
}
Now when i want to search threads i have the following code.
class SearchThreads{
public function query()
{
$searchQuery = request('q');
$onlyTitle = request()->boolean('only_title');
if (isset($searchQuery)) {
if ($onlyTitle) {
$query = Thread::search($searchQuery);
} else {
$query = Threads::search($searchQuery);
}
} else {
if ($onlyTitle) {
$query = Activity::ofThreads();
} else {
$query = Activity::ofThreadsAndReplies();
}
}
}
}
To explain the code.
If the user enters a search word ( $searchQuery) then use Algolia to search, otherwise make a database query directly.
If the user enters a search word
Use the Thread index if the user has checked the onlyTitle checkbox
Use the Threads index if the user hasn't checked the onlyTitle checkbox
If the user doesn't enter a search word
Get all the threads if the user has checked the onlyTitle checkbox
Get all the threads and replies if the user hasn't checked the onlyTitle checkbox
Is there a pattern to simplify the nested if statements or should i just create a separate class for the cases where
a user has entered a search word
a user hasn't entered a search word
And inside each of those classes to check if the user has checked the onlyTitle checkbox
I would refactor this code to this:
Leave the request parameter to unify the search methods in an interface.
interface SearchInterface
{
public function search(\Illuminate\Http\Request $request);
}
class Search {
protected $strategy;
public function __construct($search)
{
$this->strategy = $search;
}
public function getResults(\Illuminate\Http\Request $request)
{
return $this->strategy->search($request);
}
}
class SearchFactory
{
private \Illuminate\Contracts\Container\Container $container;
public function __construct(\Illuminate\Contracts\Container\Container $container)
{
$this->container = $container;
}
public function algoliaFromRequest(\Illuminate\Http\Request $request): Search
{
$type = $request['type'];
$onlyTitle = $request->boolean('only_title');
if ($type === 'thread' && !$onlyTitle) {
return $this->container->get(Threads::class);
}
if ($type === 'profile_post' && !$onlyTitle) {
return $this->container->get(ProfilePosts::class);
}
if (empty($type) && !$onlyTitle) {
return $this->container->get(AllPosts::class);
}
if ($onlyTitle) {
return $this->container->get(Thread::class);
}
throw new UnexpectedValueException();
}
public function fromRequest(\Illuminate\Http\Request $request): Search
{
if ($request->missing('q')) {
return $this->databaseFromRequest($request);
}
return $this->algoliaFromRequest($request);
}
public function databaseFromRequest(\Illuminate\Http\Request $request): Search
{
$type = $request['type'];
$onlyTitle = $request->boolean('only_title');
if ($type === 'thread' && !$onlyTitle) {
return $this->container->get(DatabaseSearchThreads::class);
}
if ($type === 'profile_post' && !$onlyTitle) {
return $this->container->get(DatabaseSearchProfilePosts::class);
}
if ($type === 'thread' && $onlyTitle) {
return $this->container->get(DatabaseSearchThread::class);
}
if ($request->missing('type')) {
return $this->container->get(DatabaseSearchAllPosts::class);
}
throw new InvalidArgumentException();
}
}
final class SearchController
{
private SearchFactory $factory;
public function __construct(SearchFactory $factory)
{
$this->factory = $factory;
}
public function listResults(\Illuminate\Http\Request $request)
{
return $this->factory->fromRequest($request)->getResults($request);
}
}
The takeaway from this is it is very important to not involve the request in the constructors. This way you can create instances without a request in the application lifecycle. This is good for caching, testability and modularity. I also don't like the app and request methods as they pull variables out of thin air, reducing testability and performance.
class Search
{
public function __construct(){
$this->strategy = app(SearchFactory::class)->create();
}
public function getResults()
{
return $this->strategy->search();
}
}
class SearchFactory
{
public function create()
{
if (request()->missing('q')) {
return app(DatabaseSearch::class);
} else {
return app(AlgoliaSearch::class);
}
}
}
class AlgoliaSearch implements SearchInterface
{
public function __construct()
{
$this->strategy = app(AlgoliaSearchFactory::class)->create();
}
public function search()
{
$this->strategy->search();
}
}
class AlgoliaSearchFactory
{
public function create()
{
if (request('type') == 'thread') {
return app(Threads::class);
} elseif (request('type') == 'profile_post') {
return app(ProfilePosts::class);
} elseif (request()->missing('type')) {
return app(AllPosts::class);
} elseif (request()->boolean('only_title')) {
return app(Thread::class);
}
}
}
Where the classes created in the AlgoliaSearchFactory are algolia aggregators so the search method can be called on any of those classes.
Would something like this make it cleaner or even worse ?
Right now i have strategies that have strategies which sounds too much to me.
I have tried to implement a good solution for you, but I had to make some assumptions about the code.
I decoupled the request from the constructor logic and gave the search interface a request parameter. This makes the intention clearer than just pulling the Request from thin air with the request function.
final class SearchFactory
{
private ContainerInterface $container;
/**
* I am not a big fan of using the container to locate the dependencies.
* If possible I would implement the construction logic inside the methods.
* The only object you would then pass into the constructor are basic building blocks,
* independent from the HTTP request (e.g. PDO, AlgoliaClient etc.)
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
private function databaseSearch(): DatabaseSearch
{
return // databaseSearch construction logic
}
public function thread(): AlgoliaSearch
{
return // thread construction logic
}
public function threads(): AlgoliaSearch
{
return // threads construction logic
}
public function profilePost(): AlgoliaSearch
{
return // thread construction logic
}
public function onlyTitle(): AlgoliaSearch
{
return // thread construction logic
}
public function fromRequest(Request $request): SearchInterface
{
if ($request->missing('q')) {
return $this->databaseSearch();
}
// Fancy solution to reduce if statements in exchange for legibility :)
// Note: this is only a viable solution if you have done correct http validation IMO
$camelCaseType = Str::camel($request->get('type'));
if (!method_exists($this, $camelCaseType)) {
// Throw a relevent error here
}
return $this->$camelCaseType();
}
}
// According to the code you provided, algoliasearch seems an unnecessary wrapper class, which receives a search interface, just to call another search interface. If this is the only reason for its existence, I would remove it
final class AlgoliaSearch implements SearchInterface {
private SearchInterface $search;
public function __construct(SearchInterface $search) {
$this->search = $search;
}
public function search(Request $request): SearchInterface {
return $this->search->search($request);
}
}
I am also not sure about the point of the Search class. If it only effectively renames the search methods to getResults, I am not sure what the point is. Which is why I omitted it.
I had to write all this to make the problem understandable.
The SearchFactory takes all the required parameters and based on these parameters, it calls either AlgoliaSearchFactory or DatabaseSearchFactory to produce the final object that will be returned.
class SearchFactory
{
protected $type;
protected $searchQuery;
protected $onlyTitle;
protected $algoliaSearchFactory;
protected $databaseSearchFactory;
public function __construct(
$type,
$searchQuery,
$onlyTitle,
DatabaseSearchFactory $databaseSearchFactory,
AlgoliaSearchFactory $algoliaSearchFactory
) {
$this->type = $type;
$this->searchQuery = $searchQuery;
$this->onlyTitle = $onlyTitle;
$this->databaseSearchFactory = $databaseSearchFactory;
$this->algoliaSearchFactory = $algoliaSearchFactory;
}
public function create()
{
if (isset($this->searchQuery)) {
return $this->algoliaSearchFactory->create($this->type, $this->onlyTitle);
} else {
return $this->databaseSearchFactory->create($this->type, $this->onlyTitle);
}
}
}
The DatabaseSearchFactory based on the $type and the onlyTitle parameters that are passed from the SearchFactory returns an object which is the final object that needs to be used in order to get the results.
class DatabaseSearchFactory
{
public function create($type, $onlyTitle)
{
if ($type == 'thread' && !$onlyTitle) {
return app(DatabaseSearchThreads::class);
} elseif ($type == 'profile_post' && !$onlyTitle) {
return app(DatabaseSearchProfilePosts::class);
} elseif ($type == 'thread' && $onlyTitle) {
return app(DatabaseSearchThread::class);
} elseif (is_null($type)) {
return app(DatabaseSearchAllPosts::class);
}
}
}
Same logic with DatabaseSearchFactory
class AlgoliaSearchFactory
{
public function create($type, $onlyTitle)
{
if ($type == 'thread' && !$onlyTitle) {
return app(Threads::class);
} elseif ($type == 'profile_post' && !$onlyTitle) {
return app(ProfilePosts::class);
} elseif (empty($type) && !$onlyTitle) {
return app(AllPosts::class);
} elseif ($onlyTitle) {
return app(Thread::class);
}
}
}
The objects that are created by AlgoliaSearchFactory have a method search which needs a $searchQuery value
interface AlgoliaSearchInterface
{
public function search($searchQuery);
}
The objects that are created by DatabaseSearchFactory have a search method that doesn't need any parameters.
interface DatabaseSearchInterface
{
public function search();
}
The class Search now takes as a parameter the final object that is produced by SearchFactory which can either implement AlgoliaSearchInterface or DatabaseSearchInterface that's why I haven't type hinted
The getResults method now has to find out the type of the search variable ( which interface it implements ) in order to either pass the $searchQuery as a parameter or not.
And that is how a controller can use the Search class to get the results.
class Search
{
protected $strategy;
public function __construct($search)
{
$this->strategy = $search;
}
public function getResults()
{
if(isset(request('q')))
{
$results = $this->strategy->search(request('q'));
}
else
{
$results = $this->strategy->search();
}
}
}
class SearchController(Search $search)
{
$results = $search->getResults();
}
According to all of #Transitive suggestions this is what I came up with. The only thing that I cannot solve is how to call search in the getResults method without having an if statement.
Related
Say I have to similar function :
public function auth(){
return $someResponse;
}
public function collect(){
return $someOtherResponse
}
Question : When one of the response get passed to another class, is there any way to check which function returned the response ?
In a purely object-oriented way, wanting to attach information to a value is akin to wrapping it into a container possessing context information, such as:
class ValueWithContext {
private $value;
private $context;
public function __construct($value, $context) {
$this->value = $value;
$this->context = $context;
}
public value() {
return $this->value;
}
public context() {
return $this->context;
}
}
You can use it like this:
function auth()
{
return new ValueWithContext($someresponse, "auth");
}
function collect()
{
return new ValueWithContext($someotherrpesonse, "collect");
}
This forces you to be explicit about the context attached to the value, which has the benefit of protecting you from accidental renamings of the functions themselves.
As per my comment, using arrays in the return will give you a viable solution to this.
It will allow a way to see what has been done;
function auth()
{
return (array("auth" => $someresponse));
}
function collect()
{
return (array("collect" => $someotherrpesonse));
}
class myClass
{
function doSomething($type)
{
if (function_exists($type))
{
$result = $type();
if (isset($result['auth']))
{
// Auth Used
$auth_result = $result['auth'];
}
else if (isset($result['collect']))
{
// Collect used
$collect_result = $result['collect'];
}
}
}
}
It can also give you a way to fail by having a return array("fail" => "fail reason")
As comments say also, you can just check based on function name;
class myClass
{
function doSomething($type)
{
switch ($type)
{
case "auth" :
{
$result = auth();
break;
}
case "collect" :
{
$result = collect();
break;
}
default :
{
// Some error occurred?
}
}
}
}
Either way works and is perfectly valid!
Letting the two user defined functions auth() & collect() call a common function which makes a call to debug_backtrace() function should do the trick.
function setBackTrace(){
$backTraceData = debug_backtrace();
$traceObject = array_reduce($backTraceData, function ($str, $val2) {
if (trim($str) === "") {
return $val2['function'];
}
return $str . " -> " . $val2['function'];
});
return $traceObject;
}
function getfunctionDo1(){
return setBackTrace();
}
function getfunctionDo2(){
return setBackTrace();
}
class DoSomething {
static function callfunctionTodo($type){
return (($type === 1) ? getfunctionDo1() : getfunctionDo2());
}
}
echo DoSomething::callfunctionTodo(1);
echo "<br/>";
echo DoSomething::callfunctionTodo(2);
/*Output
setBackTrace -> getfunctionDo1 -> callfunctionTodo
setBackTrace -> getfunctionDo2 -> callfunctionTodo
*/
The above function would output the which function returned the response
I've seen in all DDD examples, collections are implemented as classes, for example at PHPMaster website:
<?php
namespace Model\Collection;
use Mapper\UserCollectionInterface,
Model\UserInterface;
class UserCollection implements UserCollectionInterface
{
protected $users = array();
public function add(UserInterface $user) {
$this->offsetSet($user);
}
public function remove(UserInterface $user) {
$this->offsetUnset($user);
}
public function get($key) {
return $this->offsetGet($key);
}
public function exists($key) {
return $this->offsetExists($key);
}
public function clear() {
$this->users = array();
}
public function toArray() {
return $this->users;
}
public function count() {
return count($this->users);
}
public function offsetSet($key, $value) {
if (!$value instanceof UserInterface) {
throw new \InvalidArgumentException(
"Could not add the user to the collection.");
}
if (!isset($key)) {
$this->users[] = $value;
}
else {
$this->users[$key] = $value;
}
}
public function offsetUnset($key) {
if ($key instanceof UserInterface) {
$this->users = array_filter($this->users,
function ($v) use ($key) {
return $v !== $key;
});
}
else if (isset($this->users[$key])) {
unset($this->users[$key]);
}
}
public function offsetGet($key) {
if (isset($this->users[$key])) {
return $this->users[$key];
}
}
public function offsetExists($key) {
return ($key instanceof UserInterface)
? array_search($key, $this->users)
: isset($this->users[$key]);
}
public function getIterator() {
return new \ArrayIterator($this->users);
}
}
And the interface:
<?php
namespace Mapper;
use Model\UserInterface;
interface UserCollectionInterface extends \Countable, \ArrayAccess, \IteratorAggregate
{
public function add(UserInterface $user);
public function remove(UserInterface $user);
public function get($key);
public function exists($key);
public function clear();
public function toArray();
}
Why don't they just use a simple array? What benefits do you get by using a given implementation?
What benefits do you get by using a given implementation?
you can easily augument UserCollection with additional behaviour, like reacting whenever a new user is added or removed (e.g. observer pattern). using arrays, one would have to scatter such logic all over the place, while using a class you can put this logic just in one place and control it there. it is also more testable.
you can also include some invariants checking code making sure UserCollection concept always adheres to domain constraints.
even if there seem to be no immediate need for constraints or additional behaviour, these may come up later in the project life and it would be somewhat difficult to implement them in code base that wasn't designed for extensibility.
I have problem in using ExceptionMatcher...My example spec:
class DescribeBall extends \PHPSpec\Context {
private $_ball = null;
function before() {
$this->_ball = $this->spec(new Ball);
}
function itShouldHaveStatusRolledOnRoll() {
$this->_ball->roll();
$this->_ball->getStatus()->should->be('Rolled');
}
function itShouldThrowException() {
$this->_ball->getException()->should->throwException('Exception','Error');
}
}
My example class
class Ball {
private $status = null;
public function roll() {
$this->status = 'Rolled';
}
public function getStatus() {
return $this->status;
}
public function getException() {
throw new Exception('Error');
}
}
Anyone used this matcher with success?
$this->_ball->getException()->should->throwException('Exception','Error');
Thanks to my colleagues:
"The last time I looked at it, it used closures (unless Marcello changed it meanwhile) it should still work like this":
function itShouldThrowException() {
$ball = $this->_ball;
$this->spec(function() use ($ball) {
$ball->getException();
})->should->throwException('Exception','Error');
}
I need to implement the following pattern in php:
class EventSubscriber
{
private $userCode;
public function __construct(&$userCode) { $this->userCode = &$userCode; }
public function Subscribe($eventHandler) { $userCode[] = $eventHandler; }
}
class Event
{
private $subscriber;
private $userCode = array();
public function __construct()
{
$this->subscriber = new Subscriber($this->userCode)
}
public function Subscriber() { return $this->subscriber; }
public function Fire()
{
foreach ($this->userCode as $eventHandler)
{
/* Here i need to execute $eventHandler */
}
}
}
class Button
{
private $eventClick;
public function __construct() { $this->eventClick = new Event(); }
public function EventClick() { return $this->eventClick->Subscriber(); }
public function Render()
{
if (/* Button was clicked */) $this->eventClick->Fire();
return '<input type="button" />';
}
}
class Page
{
private $button;
// THIS IS PRIVATE CLASS MEMBER !!!
private function ButtonClickedHandler($sender, $eventArgs)
{
echo "button was clicked";
}
public function __construct()
{
$this->button = new Button();
$this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler'));
}
...
}
what is the correct way to do so.
P.S.
I was using call_user_func for that purpose and believe it or not it was able to call private class members, but after few weeks of development i've found that it stopped working. Was it a bug in my code or was it some something else that made me think that 'call_user_func' is able call private class functions, I don't know, but now I'm looking for a simple, fast and elegant method of safely calling one's private class member from other class. I'm looking to closures right now, but have problems with '$this' inside closure...
Callbacks in PHP aren't like callbacks in most other languages. Typical languages represent callbacks as pointers, whereas PHP represents them as strings. There's no "magic" between the string or array() syntax and the call. call_user_func(array($obj, 'str')) is syntactically the same as $obj->str(). If str is private, the call will fail.
You should simply make your event handler public. This has valid semantic meaning, i.e., "intended to be called from outside my class."
This implementation choice has other interesting side effects, for example:
class Food {
static function getCallback() {
return 'self::func';
}
static function func() {}
static function go() {
call_user_func(self::getCallback()); // Calls the intended function
}
}
class Barf {
static function go() {
call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so:
} // Error -- no function 'func' in 'Barf'
}
Anyway, if someone's interested, I've found the only possible solution via ReflectionMethod. Using this method with Php 5.3.2 gives performance penalty and is 2.3 times slower than calling class member directly, and only 1.3 times slower than call_user_func method. So in my case it is absolutely acceptable. Here's the code if someone interested:
class EventArgs {
}
class EventEraser {
private $eventIndex;
private $eventErased;
private $eventHandlers;
public function __construct($eventIndex, array &$eventHandlers) {
$this->eventIndex = $eventIndex;
$this->eventHandlers = &$eventHandlers;
}
public function RemoveEventHandler() {
if (!$this->eventErased) {
unset($this->eventHandlers[$this->eventIndex]);
$this->eventErased = true;
}
}
}
class EventSubscriber {
private $eventIndex;
private $eventHandlers;
public function __construct(array &$eventHandlers) {
$this->eventIndex = 0;
$this->eventHandlers = &$eventHandlers;
}
public function AddEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex++] = $eventHandler;
}
public function AddRemovableEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex] = $eventHandler;
$result = new EventEraser($this->eventIndex++, $this->eventHandlers);
return $result;
}
}
class EventHandler {
private $owner;
private $method;
public function __construct($owner, $methodName) {
$this->owner = $owner;
$this->method = new \ReflectionMethod($owner, $methodName);
$this->method->setAccessible(true);
}
public function Invoke($sender, $eventArgs) {
$this->method->invoke($this->owner, $sender, $eventArgs);
}
}
class Event {
private $unlocked = true;
private $eventReceiver;
private $eventHandlers;
private $recursionAllowed = true;
public function __construct() {
$this->eventHandlers = array();
}
public function GetUnlocked() {
return $this->unlocked;
}
public function SetUnlocked($value) {
$this->unlocked = $value;
}
public function FireEventHandlers($sender, $eventArgs) {
if ($this->unlocked) {
//защита от рекурсии
if ($this->recursionAllowed) {
$this->recursionAllowed = false;
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->Invoke($sender, $eventArgs);
}
$this->recursionAllowed = true;
}
}
}
public function Subscriber() {
if ($this->eventReceiver == null) {
$this->eventReceiver = new EventSubscriber($this->eventHandlers);
}
return $this->eventReceiver;
}
}
As time passes, there are new ways of achieving this.
Currently PSR-14 is drafted to handle this use case.
So you might find any of these interesting:
https://packagist.org/?query=psr-14
Please look at the following code snipped
class A
{
function __get($name)
{
if ($name == 'service') {
return new Proxy($this);
}
}
function render()
{
echo 'Rendering A class : ' . $this->service->get('title');
}
protected function resourceFile()
{
return 'A.res';
}
}
class B extends A
{
protected function resourceFile()
{
return 'B.res';
}
function render()
{
parent::render();
echo 'Rendering B class : ' . $this->service->get('title');
}
}
class Proxy
{
private $mSite = null;
public function __construct($site)
{
$this->mSite = $site;
}
public function get($key)
{
// problem here
}
}
// in the main script
$obj = new B();
$obj->render();
Question is: in method 'get' of class 'Proxy', how I extract the corresponding resource file name (resourceFile returns the name) by using only $mSite (object pointer)?
What about:
public function get($key)
{
$file = $this->mSite->resourceFile();
}
But this requires A::resourceFile() to be public otherwise you cannot access the method from outside the object scope - that's what access modifiers have been designed for.
EDIT:
OK - now I think I do understand, what you want to achieve. The following example should demonstrate the desired behavior:
class A
{
private function _method()
{
return 'A';
}
public function render()
{
echo $this->_method();
}
}
class B extends A
{
private function _method()
{
return 'B';
}
public function render()
{
parent::render();
echo $this->_method();
}
}
$b = new B();
$b->render(); // outputs AB
But if you ask me - I think you should think about your design as the solution seems somewhat hacky and hard to understand for someone looking at the code.