Laravel 5 Eloquent - how to get scope columns - php

I am trying to write a scope and I need to know if any of the previous scopes added any ->select() information earlier and if not, then add it (which would erase the previous stuff).
The scope gets a $query object which is \Illuminate\Database\Query\Builder (according to the docs anyway)
If I look into Builder.php I see the $columns property which is used to store the columns to fetch
/**
* The columns that should be returned.
*
* #var array
*/
public $columns;
However in my scope if I try to access it using
$query->columns
I get an error message saying:
PHP error: Undefined property: Illuminate\Database\Eloquent\Builder::$columns
As you can see there is some sort of magic going on behind and it is looking at Eloquent\Builder and not at Database\Query\Builder.
I am probably missing something simple here, maybe need more coffee again... Any pointers are welcome
Thanks
PS. I know I can use addSelect() but I would like to know why I am not able to access the 'columns' variable still...

The object that gets passed to your scope method is of class Illuminate\Database\Eloquent\Builder, while the class that has the columns attribute is Illuminate\Database\Query\Builder. Object of the first class uses internally object of the second class so if you want to access its public properties you need to do:
public function scopeFunction($query) {
$columns = $query->getQuery()->columns;
}
Keep in mind that some scopes can be applied after your scope is called and modify $columns attribute after your scope is run.

Related

PHP - Unit testing method with unmockable class in its body (PHPUnit)

I have a method that uses another class to calculate its outcome, that I want to test using PHPUnit.
/**
* Returns true if the given user has been granted the given permission.
*
* #param User $user
* #param AbstractPermission $permission
* #return bool
*/
public function userPermissionGranted(User $user, AbstractPermission $permission) : bool
{
// Retrieve model from database.
$user_permission = UserPermission::scopeUser($user)
->scopePermission($permission)
->first();
return $user_permission ? $user_permission->isGranted() : $permission->isGrantedByDefault();
}
Leaving out of consideration what this method actually does, I am wondering how to test this method. I can pass mocks of the User and AbstractPermission classes to the method, but the UserPermission class that is used inside the method's body (to retrieve a model from the database) I can do nothing with.
On top of that, if I pass mocks of the User and Permisson classes, they won't exist in the database, so when UserPermission queries the database, it will receive no results and the method will fail.
What do I do here? Is it considered good practice to simply mock the database (i.e. copying the live db structure and filling it with test data) and let my model query that database, and just trusting that everything is OK? Any suggestions on what to do here?
On a side note, UserPermission is an Eloquent model. I am merely making use of Eloquent here - without Laravel.
As a general rule, you can't directly mock static methods - at least, there's no good way to do it. Depending on how your application is set up, you might be able to hack something together that involves redefining the method with runkit or perhaps messing with includes/autoloader to load a mock class instead of the real one, but such solutions are kludgey at best.
One simple approach to allow unit-testing would be to wrap your static method calls in an instance method. So you'd create a new class with instance methods that call the static methods. Of course, you wouldn't be able to test that new class, but if it's a thin wrapper around the static methods then there's not really any value in testing it anyway.
So you might end up with something like this, for example:
class UserPermissionWrapper {
public function getUserPermission($user) {
return UserPermission::scopeUser($user);
}
}
Then you can inject that into your original class and get something like this:
public function userPermissionGranted(User $user, AbstractPermission $permission) : bool
{
// Assume this is an instance of UserPermissionWrapper injected at construction
$user_permission = $this->userPermissionWrapper
->getUserPermission($user)
->scopePermission($permission)
->first();
return $user_permission ? $user_permission->isGranted() : $permission->isGrantedByDefault();
}
Now you have an object calling instance methods, so you can inject a mock version of that class and set up the method calls in the normal way.
To answer my own question - and I've only come to a reasonable answer after a while of writing some more unit tests - I guess what it comes down to is this:
When testing the userPermissionGranted() method, we're actually only validating that the method works as expected. We're fetching a model from the database, and we may assume that this model has been tested already in its own, separate test. Given that we may assume that this model works as intended, and that we cannot access the database here to fetch the actual model, we can use a mock of the model, which we customly build to work just the way it should work, without actually performing any database work. That's where Peter Geer's answer comes in. Our class should contain a method to fetch the model from the database, so that instead of fetching and returning the model from the database, we can set up a mock and return that instead. In this case that means that in the return line of the method we're testing, we're going to test a mocked isGranted() on $user_permission (which is the mock we created to return a value that we want it to return), and isGrantedByDefault() on the $permission mock that we passed to the method when we called it.

Laravel choosing a class at runtime

I am trying to merge two websites created using Laravel 5 into one multisite (yes, I wasn't that experienced when making that decision). The two websites are one for cats and one for dogs.
My problem is that I have a model called Item, the one in cats is storing things in a different table than model Item in dogs.
What I have done in my controller:
protected $posts_class;
public function __construct()
{
$this->items_class = "App\\Models\\" . config('domain') . "\\Item";
}
public function index()
{
$items = $this->items_class::all();
return view('items')->with('items', $items);
}
but it keeps giving an error:
syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)
however if I do:
public function index()
{
$class= $this->items_class;
$items = $class::all();
}
it works.. but I don't want extra variables within the controller method.
I would like to know why the first one doesn't work. If anyone has any recommendations on how to make this multisite work in a better way than this one then I am open to suggestions. Thank you.
The T_PAAMAYIM_NEKUDOTAYIM operator is more commonly known as the Scope Resolution Operator. In the context of PHP, it is used to statically access class methods and variables.
The all() method is a static method on the Eloquent class your model inherits from. As such, it should be called like ClassName::all().
If I understand what you are trying to do correctly, you are trying to use a dynamic variable as the class name. Unfortunately, using $this->someVariable::all() doesn't quite work the way one would expect like that, and as you know, you have to separate it into an individual variable first.
In the spirit of answering your question directly with a way to call it without creating a separate variable, the answer is to use the often forgotten forward_static_call method.
$items = forward_static_call([$this->items_class, 'all']);
If you need to call a static method using this methodology and want to pass an array of parameters, there is also a related function forward_static_call_array().
Reference:
http://php.net/manual/en/language.oop5.paamayim-nekudotayim.php
http://php.net/manual/en/function.forward-static-call.php
http://php.net/manual/en/function.forward-static-call-array.php

Get property from an object in PHP

Have to do some php coding but I am totally new to it. The question is I was trying to get a property value from an object called $mgm_member, this object is from wordpress plugin which records website's member info. First I use:
var_dump(get_object_vars($mgm_member));
The results shows that this obeject has 37 properties and all of them have values. Than I tried use
var_dump($mgm_member->phone);
The result is null. I tried many other properties but only the first property of this object have value. Can anyone help me? Thank you very much.
well I suppose if the scope of "phone" is private var_dump will not be able to access and view it, is it? In my case, I can view all properties and their values using var_dump() function but when I tried to get one property it just doesn't work. However,I can get the first property "id" using the same code
echo $mgm_member->id;
That is really weird.
As the property is 'private' you will need to make a function to access and return it.
A dump may display them but you WILL NOT be able to directly access a 'private' property.
class .... {
public function getPhone()
{
return ($this->phone);
}
}
then:
echo $mgm_member->getPhone();
var_dump(get_object_vars($mgm_member)); shows the object variables. If you are getting data with this line of code, you have data in your object.
You can access properties of a variable in your code with $mgm_member->phone - why not do so?
If you want to place the data into a variable you can use something like this:
$myVar=$mgm_member->phone;
but that defeats the purpose of OOP. Why not refer to it as exactly $mgm_members->phone all the way through your code?
Edit: As you point out, the property is private which means that only the object itself can use it. You could get around this by modifying the object, but this may be a nasty approach - it is private for a reason. Can you not use functions within the object to display the values? Alternately, is there a function you can use in the object to return you a clone of the object with different property attributes?

Php (eval vs call_user_func vs variable functions ...)

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...

call member function by reference php

Please stop me if i am doing something wrong. It works but somehow it doesn't appear the right way to me... Look at the member function call in talks.php. Does this look right to you? Is there a better way to solve that? Thanks.
show.php
I am passing my user class by reference:
$talks = new talks($comments, $user);
talks.php:
[...]
function __construct($comments, &$user)
{
//Passing user class
$this->user = $user;
[...]
if ($this->user->is_loaded()){}
This looks a-ok to me. What problem do you see with it?
In php 5, objects are always passed by reference.
From http://www.php.net/manual/en/language.oop5.references.php:
A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
So, you should not need the "&" operator in the parameter list of your constructor.

Categories