I'm newbye of OOP and Laravel , but i have noticed that the operator with in laravel is used for a lot of things: for example i have found this part of code in the documentation:
return Redirect::to('register')->withErrors($validator);
withErrors? I know that with is used to pass multiple element to a view: is there more that i need to know about this operator?
Another Question: the validate class in php have this type of operator:
$validator->required('You must supply an email address.')->email('You must supply a valid email address')->validate('email', 'Email');
why i can use multiple "->" for a instance of the class validator? I know that in this way:
$object = new object();
$object->something(); //metod or attribute
but i know that it's impossible use the operator "-" for indicate multiple method/attribute for a class(even if it is a library).
thank you for all and i'm sorry for,maybe,the stupid question! Thank you!
Well Redirect::to() returns an instance of Illuminate\Http\RedirectResponse. This class has a method withErrors which is called in the example.
However this with* method is a bit different than the one to pass data to a view. withErrors flashes the data to the session so it is available for the next request (after the redirect happens)
Regarding your second question, this: $validator->required()->email() is called method chaining. It is used a lot (not only in Laravel) to achieve a nice and brief syntax.
What happens is that the methods return $this so you can call the next method on the same object right away.
Let's look at another example:
$result = User::where('active', true)->where('name', 'like', 'a%')->get();
If we now take a look at the where() method in Illuminate\Database\Eloquent\Builder you can see that after the where logic happens it returns $this so we can continue calling methods.
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
// code omitted for brevity
return $this;
}
Many Laravel classes implement this so-called Fluent Interface that allows method chaining for nearly every function call. Only methods that have an obvious return value can't be chained. Like retrieving a result with get(). Of course it has to return the result and it can't return $this as well.
You seem to understand the purpose of with() just fine.
As for your second question, you can "chain" object methods if the methods each return the object like this:
<?php
class MyClass {
public function myMethod()
{
// do stuff
return $this;
}
Related
I was reading a short tutorial on Laravel here. Since I am not experienced in laravel or development in general I am wondering what this part of the code does exactly:
public function approve(): User
As it is seems to me, it is the same thing as just calling the model from inside the function like so:
App\User::
What is the difference in this two approches?
The first example you shared:
public function approve(): User
is simply a feature of PHP7 which allows you to use static type programming practices with PHP. Essentially this new function signature is telling you that this function needs to return a User type or it will throw a TypeError exception.
The second example you shared:
App\User::
is using what is called the Scope Resolution Operator(::) This operator allows you to call Class Level / Static Methods. In Laravel for example, that would be something like:
App\User::Find(1);
or
App\User::Where('id', 1);
and these differ from object level methods which would be called like so:
$user = new App\User();
$user->id = 1;
$user->save()
Notice the class instance uses the -> operator.
You can learn more about what I mentioned at the following links:
https://secure.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
http://php.net/manual/en/language.oop5.paamayim-nekudotayim.php
https://laravel.com/docs/5.3/eloquent
Best of luck!
No, they're not the same. The first code is utilizing PHP 7's return type declerations.
It says that the method must return an instance of User class, or PHP will throw a TypeError for you. You can also specify the FQN of the class when defining return type declarations:
public function approve(): \App\User
It's specially useful when defining interfaces:
interface CommentsIterator extends Iterator {
function current(): Comment;
// This enforces that any implementation of current()
// method, must return an instance of Comment class.
}
See what other RFCs made their way into PHP 7:
https://secure.php.net/manual/en/migration70.new-features.php
And treat yourself with Jeffrey's screencasts on PHP 7 new features:
https://laracasts.com/series/php7-up-and-running/episodes/3
But the second code (App\User::whatever()) is just calling the static method whatever() of the App\User class. It has nothing to do with the first code example which enforces return types.
Goal: I want to "decorate" Laravel's Query Builder with additional functionality (without directly modifying it).
Example Problem: I'll try to keep this very brief. I have implemented a get method on my decorator:
public function get($columns = ['*'])
{
return $this->cache->get(implode('.', $columns), function () use ($columns) {
return $this->queryBuilder->get($columns);
});
}
I'm also delegating all calls to methods not implemented on the decorator to the Query Builder.
public function __call($method, $parameters)
{
return call_user_func_array([$this->queryBuilder, $method], $parameters);
}
Works fine when calling directly on the decorator, as you would expect. But almost everyone is used to chaining methods together when using the Query Builder.
$queryBuilder = (new CachingDecorator( new QueryBuilder , $app['cache.store'] ));
// get all users
$queryBuilder->from('users')->get();
// get one user
$queryBuilder->from('users')->first(); // <-- delegates to get() internally
Problem: The results from the call directly above are not being cached. Obviously because the from method returns an instance of the Laravel Query Builder and not my decorator.
Question: Is there some helpful pattern that will help solve this? Or is this a limitation of the decorator pattern?
My first thought was to try to bind $this to another object, like you can in Javascript. I don't think PHP allows this.
The best solution I can come up with involves a class to map the query builder object to its decorator(s), and/or some sort of base decorator that re-implements almost every method in the query builder object (not a fan of this one as it totally throws the DRY principle out the window).
Additional Notes: I know I could side-step the issue by just not chaining method calls together. No brainer right? Except it is not reasonable to ask every developer on the team to avoid chaining their calls together. I would much rather solve this problem than side-step it.
You should return your decorator from the __call method:
public function __call($method, $parameters)
{
$result = call_user_func_array([$this->queryBuilder, $method], $parameters);
return $result === $this->queryBuilder ? $this : $result;
}
If you're using PHP 5.6+, you can use the spread operator to clean this up a bit:
public function __call($method, $parameters)
{
$result = $this->queryBuilder->$method(...$parameters);
return $result === $this->queryBuilder ? $this : $result;
}
Please pardon me for not knowing what the terminology of what I'm asking for is. I don't quite know what you'd call it so bear with me.
In Laravel 4 or 5, is there some way to set a default template for an Eloquent Model? For a long time, I've been writing lines like this: (just using book system as example)
$book = Sentry::getUser()->books()->find(14);
return View::make( "books.show" )->withBook($book);
Is there any way that I can make return $book; expand into return View::make( "books.show" )->withBook($book); ?
What you're asking makes sense. However, it doesn't really fit with Laravel's view of the world -- so keep in mind you're sort of striking out on your own.
When you
return View::make( "books.show" )->withBook($book);
from a route closure or controller action, Laravel treats anything returned as a view. This include regular strings -- i.e., you can do something like
return '<p>Foo</p>';
and Laravel will render the HTML fragment.
If you try this with a regular PHP object,
$o = SomeObject;
return $o;
you'll see an error something like this
The Response content must be a string or object implementing __toString(), "object" given.
This error happens because Laravel has tried treating the returned object as a string, and PHP has no default way of rendering an object as a string.
What PHP does have is a way for you, (the programmer), to tell it (PHP) how PHP should render an object if some code (Laravel) treats that object as a string. If you add a __toString method to any object, then PHP will use this method to render a string for the object. Try the following small program
<?php
class SomeObject
{
public function __toString()
{
return 'I am a rendered SomeObject';
}
}
$object = SomeObject;
// cast the object as a string ("treat" above)
echo (string) $object;
So -- this means you can, in any object, embed the logic for rendering a view. Assuming your book class is named Book, try the following
class Book
{
//...
public function __toString()
{
return View::make( "books.show" )->withBook($this);
}
}
and then in your route closure/controller action
$book = Sentry::getUser()->books()->find(14);
return $book;
One thing to keep in mind -- since there can only be one __toString definition, if you're extending classes that implement __toString, you may break someone else's functionality. To avoid that try using some sort of conditional rendering.
Hope that helps!
Update. Per the comments below. I can't speak to the Accepts header, although my instincts say Laravel doesn't do this as there's not a culture in PHP of looking at the Accepts header. I could be wrong. As for using it yourself in __toString, I'd nix that as well -- if a user is using your object during an Accepts JSON request, but needs to render it as a non JSON string for some reason, your code would interfere with that. Better to give you object rendering contexts, and then the people who render your object choose how it renders in the route closure, controller action, or a "IoCrewrite" of the rendering methods themselves.
I'm a n00b tying to get my head around the operator syntax. I understand it's called the object operator and I can see how it's used (Where do we use the object operator "->" in PHP?) by itself.
I'm trying to learn what the purpose is when they are strung together like in this snippet (e.g. "switch($this->request->param('id')):
here's a snippet of code from a site using Kohana:
public function action_list()
{
$connections = ORM::factory('Connection')
->with('property')
->with('inviter');
switch ($this->request->param('id')) {
// more code...
}
}
It's called "method chaining". It allows you to apply more then one method, and thus do more then one thing, in one call. It's sort of the OOP equivalent of nesting functions.
It's often referred to as chaining. When a method returns an object, you can call another method on that returned object. Consider something like this:
class A {
public $numbers = 0;
public function addNumber($num) {
$this->numbers += $num;
return $this;
}
}
$a = new A();
$a->addNumber(1)->addNumber(2);
addNumber is returning an instance of itself, so you can call addNumber repeatedly.
It's often the case that a method will return an instance of another object, but the same principles apply.
Even though there's some discussions regarding this issue I wanted to check on certain example what would be the best approach.
Instead of using existing solutions I created my own persistence layer (like many do)
So my approach is also in question here.
For every table in db I have model class that has appropriate getters and setters and some mandatory methods. I also created only one generic DAO class that handles all types of model objects.
So, for example to save any model object I instantiate genericDAO class and call save method that I pass model object as attribute.
Problem is that in runtime genericDAO class doesn't know whitch model object it gets and what methods (getters and setters) exist in it, so I need to call mandatory model class method that retrieves list of attributes as multiple string array.
For example for every attribute there's array(table_column_name,attribute_name,is_string).
When I call save function it looks like this:
public function save(&$VO) {
$paramArray = $VO->getParamArray();//get array of attributes
$paramIdArray = $paramArray[0]; //first attribute is always id
/*create and execute getId() and store value into $void to check if it's save or update*/
eval('$voId = $VO->get'.ucfirst($paramIdArray[1]).'();');
...
Currently I'm using eval to execute those methods, but as it is well known eval is very slow.
I'm thinking of changing that into call_user_func method
Something like:
$voId = call_user_func(array($VO, 'get'.ucfirst($paramIdArray[1])));
But also there's other solutions. I can maybe use something like this $method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->$method();
or else
$method = 'get'.ucfirst($paramIdArray[1]));
$voId = $VO->{$method}();
What would be the best way?
First of all, there's no need to pass references like you are doing. You should give this a read to try to understand how PHP handles object references.
So public function save(&$VO) { should become public function save($VO) {.
Second, there is no need to use eval (in fact, it's better not to because of speed, debugability, etc). You can't stack-trace an eval call like you can a dynamic one.
Third, call_user_func is all but useless since PHP supports dynamic variable functions. Instead of call_user_func(array($obj, $method), $arg1), just call $obj->$foo($arg1). The call_user_func_array function is still useful since it supports variable length arguments and supports passing references.
So, ultimately, I would suggest this:
$method = 'get' . ucfirst($paramIdArray[1]);
$voId = $VO->$method();
Note that there's no need to call method_exists, since it may be callable and not exist due to __get magic method support...
I normally would use:
$method = 'get'.ucfirst($attribute);
if(method_exists($obj, $method){
$obj->$method();
}
But unless there is a very good reason i would just return a key => value array from getParamArray. And operate on that instead of using the getters...