Php Calling the functions of same class one after another - php

I want to write something like (laravel uses):
View::make('FooBarView')->with('foo', $foo)
->with('bar', $bar);
My knowledge and imagination made me to use new self instances. But I don't think that this is the best idea around and I could not handle it.
Google couldn't help me because of my bad keywords I think. I don't want to make you write code for me for sure but what is the name of this design pattern or whatever?
In laravel's source, with function uses
return $this;
But how to use it after make?
By the way, in this example; with method helps you to set variables for view's render.

To call what the function returns, the function will have to return something that is possible to call.
In this case, you could for example return "this":
class View {
/**
* #returns View
*/
public static function make($foo) {
/* do stuff, return new View instance */
return new View();
}
/**
* #returns View
*/
public function with($foo, $bar){
/* do stuff */
return $this;
}
}
That way, whenever you call with you will get the class instance back, which in turn will be callable:
View::make("foo")->with("foo")->with("bar");
// Will be same as:
$v = View::make("foo");
$v = $v->with("foo");
$v = $v->with("bar");

Related

Laravel. Is there a way to pass an API Resource as a parameter?

I'm familiar with the usage of the Laravel Api Resources, I have used it for a single element like:
return new JsonResource($theResource)
and for a collection of elements with:
return JsonResource::Collection($theResources)
But now I want to pass a jsonResource to another class or method and be able to do the same, for example to do something like:
$theJsonResource->collection($theResources)
Is there a way to do this?
In PHP you can pass classes to methods or classes as a string reference, which will help you in your case. New keyword is fairly straight forward and with the static call you have to use some PHP tricks.
class ResponseBuilder
{
private $resource;
public function __construct($resource)
{
$this->resource = $resource;
}
public function buildRessource($resource)
{
return new $this->resource($resource);
}
public function buildRessources($resources)
{
return call_user_func($this->resource .'::Collection', $resources);
}
}

Simulate a generic class in PHP

I'm trying to implement a Results class that processes queries. So, simply put, you would have functions like this:
function all();
function first();
function paginate(int $perPage, int $pageNo = 1);
This works pretty well, the problem being that the IDE has no possible way of knowing the return type when this same results class is being used in multiple different query classes. Example:
UserQuery->results()->all() will return an array of User entities.
UserQuery->results()->first() will return a single User entity.
In some languages, you have generics, which means I could just use Results<User> in the UserQuery class and then my Results class could return T[] and T respectively.
One idea I had was to pass an empty entity as the constructor to the Results class and then try to use that property as the return type, but I couldn't figure this out. Is there any workaround to this? The main problem I'm trying to solve is IDE autocompletion and analysis, so a pure phpDoc solution is perfectly fine for my use case.
The only other workaround I can come up with is having to write a separate Results class for each entity type, which would prove to be exhausting.
I don't think there's a way to do exactly what you described, but in similar cases I would suggest using a proxy class for each Results type and document proper return types using phpDocumentor's #method. This solution has added value of having a great place for any type specific Results modifications and expansions. Here's an example:
abstract class Results
{
function all(): array
{
}
function first()
{
}
function paginate(int $perPage, int $pageNo = 1): array
{
}
}
class User { }
/**
* #method User[] all()
* #method User first()
* #method User[] paginate(int $perPage, int $pageNo = 1)
*/
class UserResults extends Results { }
class UserQuery
{
/**
* #var UserResults
*/
private $results;
public function __construct()
{
$this->results = new UserResults();
}
public function results(): UserResults
{
return $this->results;
}
}
$userQuery = new UserQuery();
$test = $userQuery->results()->all();

PhpStorm not finding methods in objects created by late static binding by parent abstract class

In my PHP application I have a base class for all database objects, which is further extended by specific model classes. It goes on like this (simplified):
abstract class Model extends Collection {
(...)
function __construct(string $primary_key, string $value) {
$data = MysqlDB::instance() -> select(static::TABLE, implode(',', static::COLUMNS), "$primary_key=\"$value\"");
if (count($data) != 1)
throw new ModelException('Select condition uncertain');
parent::__construct($data[0]);
}
public static function getById(string $id) : ?Model {
try {
return new static('id', $id);
} catch (ModelException $e) {
return NULL;
}
}
(...)
}
The point is, I use the getById function on child classes to obtain the needed objects. However, since the return type of getById method is Model, PhpStorm can't figure out what methods the objects has and highlights the ones I use as an error. Consequently, no type-hinting available.
For instance, assuming that final class User extends Model and that class User has method sendNotification, this kind of code:
User::getById($id) -> sendNotification($param1, $param2, ...)
has sendNotification yellowed out as though it did not exist.
I know this isn't that much of an issue, but it's really irritating in terms of that I get distracted by constant redundant warnings and that I can't use type-hinting in such a usage case.
Try with actual PHPDoc for your getById() where you specify dynamic type (e.g. #return static or #return $this).
That's the most common way of providing such "this class and its' children" typehint.
Another possible option is kind of the same .. but using PHP 7.1 functionality (so no PHPDoc blocks).
Try using self as return type instead of Model. I mean here:
public static function getById(string $id) : ?Model {
use this instead:
public static function getById(string $id) : ?self {
But for this one you should use PhpStorm 2017.2 EAP build as 2017.1.x has issues with that (see https://stackoverflow.com/a/44806801/783119 as an example).
There are three ways to achieve this, none of them has anything to do with PHPStorm.
First overwrite the method in every child with the proper return type:
/**
* #return User
*/
public static function getById(string $id) : ?Model { return parent::getById($id); }
Second add all possible children to the return tag of the abstract class.
/**
* #return Model|User|...
*/
public static function getById(string $id) : ?Model { /* ... */ }
Third add a type hint just in place:
/** #var User $user */
$user = User::getById($id);
$user->sendNotification($param1, $param2, ...);
These are not nice and hard to maintain. But there is no "setting" in PHPStorm for that.

PHP Chain with Parent Function

I'm wanting to chain a method with the parent function of itself.
Example:
class Query
{
protected $limit;
/**
* Returns some third object that isn't in this family.
* This object represents the results, and also has
* a first function that gets called in a chain.
*/
public function get()
{
// Do Stuff
return new /* ... */;
}
public function take($amount)
{
$this->limit = $amount;
return $this;
}
}
class ChildQuery extends Query
{
protected $singular = false;
public function get()
{
if($this->singular)
return $this->take(1)->parent::get()->first();
return parent::get()
}
public function singular()
{
$this->singular = true;
return $this;
}
}
This obviously isn't the full set of functions, nor does it work, but you get the idea. I'd like ChildQuery::get to be able to call Query::get in a chain.
Right now, I have to do this:
public function get()
{
$this->take(1);
parent::get()->first();
}
Which is not appealing to me. Any ideas?
I'm running PHP 7, if it matters.
My end result would look something like this:
$query->singular()->get(); // ($query is a ChildQuery)
It is just not possible to call a parent method by the public interface of an object (even if it is the same class/object like the current context). Please have also a look at https://stackoverflow.com/a/11828729/2833639.
In my opinion your solution is the right way to go.
Off Topic: I recommend to read https://ocramius.github.io/blog/fluent-interfaces-are-evil/ to evaluate whether a fluid interface is good for your use case.

PHPUnit Test How Many Times A Function Is Called

I'm working on a test in phpunit and I'm running into an issue. I have a public function on my class that I am trying to test. Depending on the parameters passed in to the method, a protected function also in my test class will be called one or two times. I currently have a test in place to check that the return data is correct, but I would also like to make sure the protected method is being called the correct number of times.
I know that a mock object will allow me to count the number of times a function is called, but it will also override the value returned by the protected function. I tried using a mock object with no "will" section, but it would just return null, not the actual value for the protected method.
ExampleClass
public function do_stuff($runTwice){
$results = do_cool_stuff();
if($runTwice){
$results = 2 * do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff()
{
return 2;
}
In my test, I want to check whether do_cool_stuff() was called once or twice, but I still want the return values of both functions to be the same so I can test those as well in my unit test.
tl;dr
I want to count the number of times a protected method in my test object is called (like you can do with a mock object) but I still want all the methods in my test method to return their normal values (not like a mock object).
Alternatively, revert back to rolling your own testable stand-in. The following aint pretty, but you get the idea:
class ExampleClass {
public function do_stuff($runTwice) {
$results = $this->do_cool_stuff();
if ($runTwice) {
$results = 2 * $this->do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff() {
return 2;
}
}
class TestableExampleClass extends ExampleClass {
/** Stores how many times the do_cool_stuff method is called */
protected $callCount;
function __construct() {
$this->callCount = 0;
}
function getCallCount() {
return $this->callCount;
}
/** Increment the call counter, and then use the base class's functionality */
protected function do_cool_stuff() {
$this->callCount++;
return parent::do_cool_stuff();
}
}
class ExampleClassTest extends PHPUnit_Framework_TestCase {
public function test_do_stuff() {
$example = new ExampleClass();
$this->assertEquals(2, $example->do_stuff(false));
$this->assertEquals(4, $example->do_stuff(true));
}
public function test_do_cool_stuff_is_called_correctly() {
// Try it out the first way
$firstExample = new TestableExampleClass();
$this->assertEquals(0, $firstExample->getCallCount());
$firstExample->do_stuff(false);
$this->assertEquals(1, $firstExample->getCallCount());
// Now test the other code path
$secondExample = new TestableExampleClass();
$this->assertEquals(0, $secondExample->getCallCount());
$secondExample->do_stuff(true);
$this->assertEquals(2, $secondExample->getCallCount());
}
}
I wonder though whether counting the number of times a protected method has been called is really a good test. It's coupling your test to the implementation pretty hard. Does it really matter whether it is called twice, or are you more interested in the interactions with other objects? Or maybe this is pointing towards do_cool_stuff needing a refactor into two separate methods:
class ExampleClass {
public function do_stuff($runTwice) {
if ($runTwice) {
return $this->do_cool_stuff_twice();
} else {
return $this->do_cool_stuff_once();
}
}
//...
}
Try setting a global variable prior to utilizing the class.
$IAmDeclaredOutsideOfTheFunction;
then use it to store the count and simply check it after your functions and classes have been called.

Categories