I have question how can I cleverly handle a conditional where issue.
I have n field filters, e.g.: age, name, sex etc.
And now I can handle this using a dynamic scope:
public function scopeAge($query, $age)
{
if ($age) {
return $query->where('age', $age);
}
return $query;
}
and for the other fields so on.
But I need more generic example (write for all fields are a little bit cumbersome). It's possible to figure something using Laravel? I think about class e.g.: UserQuery.
class UserQuery
{
private $query;
public function __construct($query)
{
$this->query = $query;
$this->build();
}
private function build()
{
$this->appendAge();
$this->appendName();
}
private function appendAge()
{
$age = Input::get('age');
if ($age) {
$this->query->where('age', $age);
}
}
private function appendName()
{
$name = Input::get('name');
if ($name) {
$this->query->where('name', $name);
}
}
public function toQuery()
{
return $this->query;
}
}
And in model could be called:
public function scopeFilters($query)
{
$userQuery = new UserQuery($query)
return $userQuery->toQuery();
}
This is only my idea, maybe someone has better.
Related
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.
I wanna create class for making fields, but in Laravel Nova this classes called like Text::make()->placeholder() and etc. This means that methods inside that class is static.
This is my field class:
class Field
{
private $field = [];
public function set($key, $value)
{
$this->field[$key] = $value;
}
public function get()
{
return $this->field;
}
}
class Text
{
private static $field;
public static function make($name)
{
self::$field = new Field;
self::$field->set('#saturn_type', 'string');
self::$field->set('#saturn_key', $name);
self::$field->set('#saturn_field', 'text');
return new Text;
}
public function placeholder($value)
{
self::$field->set('placeholder', $value);
return $this;
}
public function required()
{
self::$field->set('required', true);
return $this;
}
public function translate()
{
self::$field->set('translate', true);
return $this;
}
public function wysiwyg()
{
self::$field->set('wysiwyg', true);
return $this;
}
public function get()
{
return (array) self::$field->get();
}
}
and this is how i call it:
$fields = [
Text::make('name')->placeholder('Full Name'),
Text::make('email')->placeholder('Email'),
Text::make('password')->placeholder('Password'),
]
$lastArray = $fields->map(function ($field) {
return $field->get();
}
);
But when i call get() method for each item in this array to get array, each item returns the last item's name and placeholder because it is static. How can i solve this.
I found the solution. Actually my friend Nijat found ))
class Field
{
private $field = [];
public function set($key, $value)
{
$this->field[$key] = $value;
}
public function get()
{
return $this->field;
}
}
class Text
{
private $field;
public function __construct($name)
{
$this->field = new Field;
$this->field->set('#saturn_type', 'string');
$this->field->set('#saturn_key', $name);
$this->field->set('#saturn_field', 'text');
}
public static function make($name)
{
return new Text($name);
}
public function placeholder($value)
{
$this->field->set('placeholder', $value);
return $this;
}
public function required()
{
$this->field->set('required', true);
return $this;
}
public function translate()
{
$this->field->set('translate', true);
return $this;
}
public function wysiwyg()
{
$this->field->set('wysiwyg', true);
return $this;
}
public function get()
{
return (array) $this->field->get();
}
}
You just have to create constructor then call it in static method.
You can make Text inherit from Field and have a much simpler make method:
class Field
{
public static function make(...$arguments)
{
return new static(...$arguments);
}
// ...
}
This will instantiate the parent class (e.g. Text) and return it, allowing you to keep the chaining. In the methods you can then use $this-> as usual (instead of self::). And move the content of Text::make to the constructor:
class Text extends Field
{
public function __construct($name)
{
$this->set('#saturn_type', 'string');
$this->set('#saturn_key', $name);
$this->set('#saturn_field', 'text');
}
// ...
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm trying to grasp the concept of a datamapper (I hope this is the right terminology) in conjunction with protected properties.
I am building an authentication system. There I have a User class
class User {
protected $id;
public $first_name;
public $mail;
protected $password;
As you can see, I chose to make $id and $password protected. Actually I'm not quite sure if that's right, but I did read, that one should try to keep the scope of properties as closed as possible.
I also build a datamapper to save my user object to my database. The mapper is injected to the user class via constructor dependency injection. I call the mappers save-method from inside my user class this way
public function save () {
return $this->dep['mapper']->saveUser($this);
}
Inside my mappers saveUser()-method I am building an array of values to pass along to my database class.
public function saveUser($obj) {
$insert_array;
foreach ( $obj as $key => $value ) {
$insert_array[$key] = $obj->get($key);
}
This does not work the way it's intended, because my mapper is not able to iterate over the protected properties. Therefore these properties are not passed on to the database. If the said properties are public it works just fine.
So my question is: How do I have to setup my classes and methods so that my mapper is able to get all the values it needs, without exposing all my properties?
Extra: I already made use of __get() to circumvent the problem, but is that good coding practice?
There is no single right answer for this, but IMO you don't want to have different visibility for fields in a data object. Here are some ideas.
If you're set on having different visibility for fields on you User class, you can change things up like this to allow your Mapper to save the data using an array you build in the save method of your user class.
<?php
class User
{
protected $id;
public $first_name;
public $mail;
protected $password;
private $dep = [];
public function __construct()
{
$this->dep['mapper'] = new Mapper();
}
public function save()
{
$data = [
'id' => $this->id,
'first_name' => $this->first_name,
'mail' => $this->mail,
'password' => $this->password
];
return $this->dep['mapper']->saveUser($data);
}
}
class Mapper
{
public function saveUser($data)
{
foreach($data as $field=>$value)
{
echo $field.': '.$value.PHP_EOL;
}
}
}
$myUser = new User();
$myUser->first_name = 'Lando';
$myUser->mail = 'lando#cloudcity.gov';
$myUser->save();
A more formal option is to use a Data Transfer Object (DTO), which is a dead-simple class that just encapsulates the data. Then you can control access to the fields in your business object.
<?php
class User
{
private $dto;
private $dep = [];
public function __construct(UserDto $dto)
{
$this->dto = $dto;
$this->dep['mapper'] = new Mapper();
}
public function __get($propName)
{
if($propName=='password')
{
throw new Exception('No password for you');
}
elseif(property_exists($this->dto, $propName))
{
return $this->dto->$propName;
}
throw new InvalidArgumentException('No property '.$propName.' found in object');
}
public function __set($propName, $value)
{
if($propName=='id')
{
throw new Exception('ID may not be changed');
}
elseif($propName=='password')
{
throw new Exception('Password may not be changed');
}
elseif(property_exists($this->dto, $propName))
{
$this->dto->$propName = $value;
}
else
{
$this->$propName = $value;
}
}
public function __isset($propName)
{
return (property_exists($this->dto, $propName));
}
public function save()
{
return $this->dep['mapper']->saveUser($this->dto);
}
}
class UserDto
{
public $id;
public $first_name;
public $mail;
public $password;
}
class Mapper
{
public function saveUser(UserDto $dto)
{
foreach ($dto as $key => $value)
{
$insert_array[$key] = $dto->$key;
echo $key.': '.$value.PHP_EOL;
}
}
}
try
{
$dto = new UserDto();
$myUser = new User($dto);
$myUser->first_name = 'Lando';
$myUser->mail = 'lando#cloudcity.gov';
echo $myUser->password;
$myUser->password = 'foobar';
$myUser->save();
}
catch(Exception $e)
{
echo $e->getMessage().PHP_EOL;
}
A better option to control access to properties is by using get/set/has methods. This is verbose, but has the benefit of adding logic or transforms to the data as you get and set it. One of the major benefits of this approach is that full-featured code editors will code-complete all of these getters and setters, you don't get that with magic methods. You can of course use this in combination with DTOs.
<?php
class User
{
private $data = [
'id'=>null,
'first_name'=>null,
'mail'=>null,
'password'=>null
];
private $dep = [];
public function __construct($data)
{
$validData = array_intersect_key($data, $this->data);
foreach($validData as $currKey=>$currValue)
{
$this->data[$currKey] = $currValue;
}
$this->dep['mapper'] = new Mapper();
}
public function getId()
{
return $this->data['id'];
}
//Notice there is no setter for ID!
public function hasId()
{
return (!empty($this->data['id']));
}
public function getFirstName()
{
return $this->data['first_name'];
}
public function setFirstName($val)
{
$this->data['first_name'] = $val;
}
public function hasFirstName()
{
return (!empty($this->data['first_name']));
}
public function getMail()
{
return $this->data['mail'];
}
public function setMail($val)
{
$this->data['mail'] = $val;
}
public function hasMail()
{
return (!empty($this->data['mail']));
}
//Notice there is no getter for ID!
public function setPassword($val)
{
$hashed = md5($val); //Just an example, don't do this
$this->data['password'] = $hashed;
}
public function hasPassword()
{
return (!empty($this->data['password']));
}
public function save()
{
return $this->dep['mapper']->saveUser($this->data);
}
}
class Mapper
{
public function saveUser($data)
{
foreach($data as $field=>$value)
{
echo $field.': '.$value.PHP_EOL;
}
}
}
try
{
$dataFromDb = [
'id'=>123,
'first_name'=>'Lando',
'mail'=>'lando#cloudcity.gov',
];
$myUser = new User($dataFromDb);
$myUser->setFirstName('Chewie');
$myUser->setMail('wookie#kashyyyk.net');
if(!$myUser->hasPassword())
{
$myUser->setPassword('AAAAAARRRRRRGHHHH');
}
$myUser->save();
}
catch(Exception $e)
{
echo $e->getMessage().PHP_EOL;
}
I prefer to do something like this, where all of the verbose boilerplate is relegated to data access objects that encapsulate the data and handle loading and saving individual records, and the app logic for individual records is contained in the main business object. They can be superclasses or traits, whatever floats your boat. Personally, I have code that writes all of my DAO and business object classes for me based on database schemas, so all I have to worry about is app logic.
<?php
trait UserDao
{
private $data = [
'id'=>null,
'first_name'=>null,
'mail'=>null,
'password'=>null
];
private $deps;
public function getId()
{
return $this->data['id'];
}
//Notice there is no setter for ID!
public function hasId()
{
return (!empty($this->data['id']));
}
public function getFirstName()
{
return $this->data['first_name'];
}
public function setFirstName($val)
{
$this->data['first_name'] = $val;
}
public function hasFirstName()
{
return (!empty($this->data['first_name']));
}
public function getMail()
{
return $this->data['mail'];
}
public function setMail($val)
{
$this->data['mail'] = $val;
}
public function hasMail()
{
return (!empty($this->data['mail']));
}
private function _getPassword()
{
return $this->data['password'];
}
private function _setPassword($val)
{
$this->data['password'] = $val;
}
public function hasPassword()
{
return (!empty($this->data['password']));
}
public function load($data)
{
$validData = array_intersect_key($data, $this->data);
foreach($validData as $currKey=>$currValue)
{
$this->data[$currKey] = $currValue;
}
}
private function _save()
{
return $this->dep['mapper']->saveUser($this->data);
}
}
class User
{
use UserDao;
public function __construct()
{
$this->dep['mapper'] = new Mapper();
}
public function setPassword($val)
{
$hashed = str_rot13($val); //Just an example, don't do this
$this->_setPassword($hashed);
}
public function getPassword()
{
return str_rot13($this->_getPassword()); //Just an example, don't do this
}
public function save()
{
echo 'Do some complex validation here...'.PHP_EOL;
$this->_save();
}
}
class Mapper
{
public function saveUser($data)
{
foreach($data as $field=>$value)
{
echo $field.': '.$value.PHP_EOL;
}
}
}
try
{
$dataFromDb = [
'id'=>123,
'first_name'=>'Lando',
'mail'=>'lando#cloudcity.gov',
];
$myUser = new User();
$myUser->load($dataFromDb);
$myUser->setFirstName('Chewie');
$myUser->setMail('wookie#kashyyyk.net');
if(!$myUser->hasPassword())
{
$myUser->setPassword('AAAAAARRRRRRGHHHH');
}
$myUser->save();
echo 'Unfutzed Password: '.$myUser->getPassword().PHP_EOL;
}
catch(Exception $e)
{
echo $e->getMessage().PHP_EOL;
}
I recommend doing some research on this subject, there are a lot of patterns, and everyone has different opinions.
What is the right way to chain custom repository methods in order to create one composed query that returns the entities based on multiple conditions.
Here is what I did:
use Doctrine\ORM\EntityRepository;
class ingredientRepository extends EntityRepository
{
private $query;
public function initNewQuery()
{
$this->query = $this->createQueryBuilder('i')->join('i.product', 'p');
return $this;
}
public function clearQuery()
{
$this->query = null;
return $this;
}
public function getResult(){
$r = $this->query->getQuery()->getResult();
$this->clearQuery();
return $r;
}
public function filterByProductName( $productName )
{
if(!$this->query) $this->initNewQuery();
$this->query->andWhere('p.name LIKE :name')->setParameter('name', '%'.$productName.'%');
return $this;
}
public function filterByMinContenu( $contenu )
{
if(!$this->query) $this->initNewQuery();
$this->query->andWhere('p.contenu > :contenu')->setParameter('contenu', $contenu);
return $this;
}
public function filterByType( $type ){
if(!$this->query) $this->initNewQuery();
$this->query->andWhere('i.type = :type')->setParameter('type', $type);
return $this;
}
}
This allows me to do things like:
$ingredients = $em->getRepository('ingredient')->initNewQuery()->filterByType(4)->getResult();
$ingredients = $em->getRepository('ingredient')->initNewQuery()->filterByProductName('ell')->filterByMinContenu(10)->getResult();
Which is exactly what I want !
But ... I don't really like it.
First, this is not how the default methods like findBy and so on work. These all return the results directly. So that doesn't seem right to mix those two behaviours.
And secondly, when calling only the filterByType method and then return the results, the query will use a join that it doesn't need. I guess I could find a way to do the join only if I need it, but again, doesn't seem right.
Any ideas on how to do this clean an simple ?
Fast answer without testing:)
use Doctrine\ORM\EntityRepository;
class ingredientRepository extends EntityRepository
{
private $query;
private function initNewQuery()
{
$this->query = $this->createQueryBuilder('i');
}
public function findBy($filterArray)
{
$this->initNewQuery();
if (array_key_exists('productName', $filterArray)) {
$this->filterByProductName($filterArray['productName']);
}
if (array_key_exists('minContenu', $filterArray)) {
$this->filterByMinContenu($filterArray['minContenu']);
}
if (array_key_exists('type', $filterArray)) {
$this->filterByType($filterArray['type']);
}
return $this->query->getQuery()->getResult();
}
private function filterByProductName($productName)
{
$this->query->join('i.product', 'p');
$this->query->andWhere('p.name LIKE :name')->setParameter('name', '%'.$productName.'%');
}
private function filterByMinContenu($contenu)
{
$this->query->andWhere('p.contenu > :contenu')->setParameter('contenu', $contenu);
}
private function filterByType($type)
{
$this->query->andWhere('i.type = :type')->setParameter('type', $type);
}
}
call:
$ingredients = $em->getRepository('ingredient')->findBy(['type'=>4]);
$ingredients = $em->getRepository('ingredient')->findBy(['productName'=>'ell', 'minContenu' => 10]);
If I have the following class example:
<?php
class Person
{
private $prefix;
private $givenName;
private $familyName;
private $suffix;
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
public function getPrefix()
{
return $this->prefix;
}
public function setGivenName($gn)
{
$this->givenName = $gn;
}
public function getGivenName()
{
return $this->givenName;
}
public function setFamilyName($fn)
{
$this->familyName = $fn;
}
public function getFamilyName()
{
return $this->familyName;
}
public function setSuffix($suffix)
{
$this->suffix = $suffix;
}
public function getSuffix()
{
return $suffix;
}
}
$person = new Person();
$person->setPrefix("Mr.");
$person->setGivenName("John");
echo($person->getPrefix());
echo($person->getGivenName());
?>
I there a way in PHP (5.4 preferably), to combine these return values into one function, this way it models a little bit more like the revealing module pattern in JavaScript?
UPDATE:
OK, I am now beginning to learn that within PHP, it is normative to return a single value from a function, but you "can" return an array of multiple values. This is the ultimate answer to my question and what I will dive into some practices with this understanding.
small example -
function fruit () {
return [
'a' => 'apple',
'b' => 'banana'
];
}
echo fruit()['b'];
Also an article I ran across on stackoverflow on the topic...
PHP: Is it possible to return multiple values from a function?
Good luck!
You sound like you want the __get() magic method.
class Thing {
private $property;
public function __get($name) {
if( isset( $this->$name ) {
return $this->$name;
} else {
throw new Exception('Cannot __get() class property: ' . $name);
}
}
} // -- end class Thing --
$athing = new Thing();
$prop = $athing->property;
In the case that you want all of the values returned at once, as in Marc B's example, I'd simplify the class design for it thusly:
class Thing {
private $properties = array();
public function getAll() {
return $properties;
}
public function __get($name) {
if( isset( $this->properties[$name] ) {
return $this->properties[$name];
} else {
throw new Exception('Cannot __get() class property: ' . $name);
}
}
} // -- end class Thing --
$athing = new Thing();
$prop = $athing->property;
$props = $athing-> getAll();
Perhaps
public function getAll() {
return(array('prefix' => $this->prefix, 'givenName' => $this->giveName, etc...));
}