Laravel - Eloquent: Polymorphic relations with namespace - namespaces

My situation is: a Calendar belongs to a Customer or Salesman
Because I also have classes like Event and File, I used the namespace App\Models for all my model classes.
so I set up the polymorphic relation:
in Calender.php
public function user() {
return $this->morphTo();
}
in Customer.php and Salesman.php
public function calendars() {
return $this->morphMany('App\Models\Calendar', 'user');
}
Now when i do
$calendar= Calendar::find(1); //calendar from a salesman
$calendar->user; //error here
...
I get this error message:
Symfony \ Component \ Debug \ Exception \ FatalErrorException
Class 'salesman' not found
I noticed that 'salesman' is low cased, maybe this is the problem?
and this is what I get from Laravels stacktrace
open: /var/www/cloudcube/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
// foreign key name by using the name of the relationship function, which
// when combined with an "_id" should conventionally match the columns.
if (is_null($foreignKey))
{
$foreignKey = snake_case($relation).'_id';
}
$instance = new $related; //HIGHLIGHTED
I had a similar error before on this line, when I was messing with the namespaces, so I guess it has something to do with that. Is there any way I can tell the morphTo() method to use the correct namespace?
Or is it something else causing this issue?
Also found this solution, but can't seem to get it working:
Polymorphic Eloquent relationships with namespaces

I found a solution that worked for me.
I always define relationships with the correct namespace, for example in Calendar:
public function events() {
return $this->hasMany('App\Models\Event');
}
My problem consisted out of 2 complications:
$calendar->user() with the morphTo(...) function was not working because my models were in a namespace, and morphTo(...) had no way of giving this namespace.
$salesman->calenders()->get() returned and empty list, although my relations in the database were there. I found out this is because of bindings with the query.
Solution for 1. : Writing a custom morphTo(...) function in Calendar to override the one of Laravel. I used the source of Laravels morphTo(...) as a base. The final statement of this function is return $this->belongsTo($class, $id);
There $class must be the namespaced class name. I used basic string operations to pull that off.
Solution for 2. : Writing a custom morphMany(...) function in Salesman and letting it return a MyMorphMany(...) similar to what Polymorphic Eloquent relationships with namespaces described.
The problem here is that $query that is passed to the MyMorphMany constructor has the wrong (namespaced) binding. It will look for where user_type = "App\\Models\\Salesman".
To fix this I used a custom getResults() function in MyMorphMany which overrides the default Laravels implementation, there I changed the bindings to use the correct, un-namespaced lower cased, class name. Then I called this getResults() function in the get() function of the MyMorphMany class.
I used $query->getBindings() and $query->setBindings() to correct the bindings.
Hope this saves someone else a few days of work, like it would have saved me :)

Related

CakePHP static name of controller

I'm probably missing something really obvious here, but is there a function in CakePHP (I'm on 3.8) that returns the name of a controller without creating an instance of the class?
An instanced controller can call this function:
echo $this->name;
But what I'd like to be able to do, is avoid typing the controller name as a string in, say, an HTML->link(); ie a static call something like:
echo $this->Html->link(
'Dashboard',
['controller' => DashboardsController::name, 'action' => 'index']
);
The reason is that I'm refactoring a couple of controllers and am having to find and replace all of those strings by hand. I come from a .Net background and CakePHP is pretty new to me, so if there's a better (more cakeish) way to carry out the refactoring than the question I'm asking, then I'd be really glad to hear it.
Nothing in the documents is leaping out at me, but I've a feeling there should be a simple answer.
The namespace of a class can be retrieved using ::class property. Checkout the following example:
DashboardsController::class // Cake/Controllers/DashboardController
The name without the namespace can be retrieved with ReflectionClass:
$function = new \ReflectionClass(DashboardsController::class);
var_dump($function->inNamespace());
var_dump($function->getShortName());
Shortname can be used to get the class without namespace:
namespace App;
class Test {
public static function name(){
$function = new \ReflectionClass(self::class);
return $function->getShortName();
}
}
var_dump(Test::name());
Checkout the docs: https://www.php.net/manual/en/language.oop5.constants.php#example-186
Reflection: https://www.php.net/manual/en/reflectionclass.getname.php

Meaning of Yii::$app->user->identity->role->role_name;

In a book called Yii2 for Beginners, which is mainly about the advanced template, I have encountered the following unexplained code, which seems relevant to RBAC:
$userHasRoleName = Yii::$app->user->identity->role->role_name;
What exactly does this mean? For example, I guess that this:
Yii::$app->user
refers to this file:
vendor\yiisoft\yii2\web\User.php
Is this correct?
In any case, what does the rest of the code refer to? Specifically:
->identity->role->role_name
In the above User.php file, I have not been able to find anything like "function identity()", so it can't be that. I have found numerous $identity variables, but I don't know which one the code might be referring to. And there is no $role variable at all.
What is this code referring to:
Yii::$app->user->identity->role->role_name;
Yii described magic methods like __get, __set and so on, to get access for inaccessible properties. Oftenly such methods begins from get or set (in Yii implementation it is). To get access to ->identity, \yii\web\User has method getIdentity. This method return identity wich you described in config with identityClass property for user component. Oftenly identityClass is a AR model which implements IdentityInterface.
'components' => [
'user' => [
'identityClass' => 'common\models\User',
]
]
To get access to ->role for example you must to create a new method
namespace common\models;
class User extends ActiveRecord implements IdentityInterface {
public function getRole(){
// if user can have only one role
return current( \Yii::$app->authManager->getRolesByUser( $this->id ) );
}
}
Btw implementation of ->role->role_name may be very different.

How to determine if a Class object represents a class or an interface in actionscript 3.0

I am trying to develop a method in actionscript that takes a Class object as a parameter and will generate an instance of that class at runtime:
public function getComponent(componentType:Class):Object
{
return new componentType();
}
In some cases I may be passed a Class object that represents an interface instead. Naturally I cannot directly create an instance of an interface.
Is there some way to query the Class object and determine whether or not it represents an interface so that I can avoid trying to create an instance in this way?
Assume you follow a naming convention for your interface class, as in the standard IInterfaceName, name of the interface prefixed with a capital "I" following a capital letter and the rest of the class name. There are a couple of ways to do this. If you have a lot of different interfaces that could be passed as a parameter you could do this:
First import the flash.utils.getQualifiedClassName and in your function:
public function getComponent(componentType:Class):Object
{
var name:String = getQualifiedClassName( componentType ).replace(/.*\:+/g,"");
if ( name.search( /^\I[A-Z]/g ) != -1 ) {
trace( "parameter is an interface!" );
return null;
}
return new componentType();
}
You don't have to set the name variable, but this helps make the search a little more strict. You could just do this instead:
if ( getQualifiedClassName( componentType ).search( /\I[A-Z]/g ) != -1 ) {
trace( "parameter is an interface!" );
return null;
}
If you are not already aware, getQualifiedClassName returns the string format of the class name. The regular expressions check specifically for the capital IInterfaceName styled string.
Lastly, if you know it's only one interface, you could simple just do this:
if ( componentType == IMyinterface ) {
trace( "component is a IMyinterface" );
}
After some careful trial and error, I've come up with the following function:
public static function isInterface(clazz:Class):Boolean
{
return describeType(clazz).factory.extendsClass.(#type=="Object").length()==0;
}
Does anyone know if this condition always holds true for all actionscript interfaces?
I think this will work for you:
getQualifiedSuperclassName(classObject);
This will return null if you pass an interface, as interfaces does not have a superclass. It will always give you ANY kind of result for a class, as every class is basically an Object :)
Anyway, I don't think that passing such mixed values to a function is a good idea ;) And checking if the class starts with "I" is worse (sorry Bennett :))
p.s.
Keep in mind that describeType is EXTREMELY slow!

Doctrine 2 namespace issue

I'm using Zend Framework 1 with the Bisna library to integrate Doctrine 2. I generated my Entities from my database model with the Doctrine 2 CLI. This is all working fine, except for the setter methods for associated records. The argument they accept must be of a specific namespace (\Category here).
class Article
{
public function setCategory(\Category $category = null) {
$this->category = $category;
return $this;
}
}
However, when I do this:
$article = $this->em->getRepository('\Application\Entity\Article')->find(1);
$category = new \Application\Entity\Category();
$category->SetName('New Category');
$article->setCategory($category);
I get the following fatal error: Argument 1 passed to Application\Entity\CategoryField::setCategory() must be an instance of Category, instance of Application\Entity\Category given.
When I change the setter method to accept \Application\Entity\Category objects, it's working of course. Should I do this for every generated method, or are there other options? This is the first time I'm using namespaces, so it might be something simple.
You can always add this to the top of your class file: use \Application\Entity\Category; and then simply reference it later like so: public function setCategory(Category $category = null)
Check out: http://php.net/manual/en/language.namespaces.importing.php for more info about use
Otherwise you would have to reference the full namespace otherwise your application does not know that \Category is a reference to \Application\Entity\Category

Referencing getter/setter functions in actionscript 3

How does one get a reference the the getter and setter functions in actionscript 3?
if a method is defined on the calls, e.g.
public function blah():String { ...}
I can get a reference to it by just saying blah or this.blah
How do get a reference to
public function get blah2():String {}
public function set blah2(b:String):void {}
Thanks!
Original response:
Unfortunately, you will not be able to store references to those as functions. The getter and setter methods are actually built around the idea that you shouldn't be able to and they therefore function as a property.
Is there a reason that you need to reference the functions specifically?
The comment I'm responding to:
I want to dynamically add external interface methods based on custom metadata tags, e.g. [External]. I was able to do this for the regular methods, but I'm trying to extend this to getter/setters as well. To do this, I need to get a reference to the function dynamically, so I can execute it with the right args using the apply function.
I think you're better off using a multi-step approach in that case. Since getters and setters function as a property and not a method, it would make sense to test to see if it is a property and then simply assign it a value directly. Would you be able to use this:
if( foo.blah2 is Function )
{
foo.blah2.apply( foo, arr );
}
else
{
foo.blah2 = arr[ 0 ];
}