Laravel choosing a class at runtime - php

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

Related

Laravel 5 Eloquent custom constructor not working

I'm trying to get a custom constructor to work with a model extending an Eloquent model in Laravel 5.4
I already make sure to call the parent constructor, but it seems that nothing that I do takes any effect at all after that.
Here is my __construct function:
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->users();
}
And here is the users() method:
public function users()
{
$this->users = collect();
foreach($this->employees as $employee) {
$this->users = $this->users->push($employee->user);
}
$this->users = $this->users->unique();
}
In this example employee is a class that links a user to a store and also defines their jobs. However, it doesn't matter what I try to assign. I have also tried just assigning a garbage variable in the constructor with
$this->foo = 'bar';
or even trying to overwrite an attribute, such as
$this->name = 'foobar';
to no avail. I've also tried to simply switch the order of the code calling parent::__construct() before or after my code and nothing at all changes.
Any help would be greatly appreciated!
If changes in your child class are not showing up, even when you load garbage into it, it sounds like it to me that you might not be loading the right class into the context?
Perhaps check your use statements at the top to make sure you loaded the child class and not the parent. It would be easy not to notice, since they share the same interface, no syntax error would be called inherently.
If that's all OK, then I would check the logs for anything suspicious (e.g. error messages related to the function).
As well, it's very useful to use the dd() function along the execution chain to see the type and contents of your variables. You may find it being overridden at some unexpected point.
So, I'm an idiot, basically. The constructor functionality was working perfectly. My problem was I was trying to set attributes not on a new instance of a class, but after retrieving it, which worked out better by just using the getAttribute methods.
Thank you everyone for trying to help, but it was just me being dumb.

How to pass a variable into multiple functions

In my screenshot below you can see I have a list of functions that run a routine, fairly in-depth routine.
Previously, I have ben repeating this routine in multiple classes, but now I would like to consolidate those multiple classes into one class and execute only one function, by passing a variable into that function to determine the output to return.
I know how to pass the variable into "one" function, but how can I pass the variable ($this_id) into my multiple functions below? Basically, whatever $this_id is from get_output($this_id); I want that same variable value to be carried over into the other $this_id functions. See screenshot...
I searched online and all answers I've seen show how to do this in a non static way, but I'm only familiar with calling things statically, really. I tried the obj way, but couldn't get it to work.
Example, execution...
$header = 'CustomTheme_output';
$header::get_output('header');
(please disregard any lose code, the code is what I have so far from trying multiple ways. private $id and __construct are from the online solutions I have been trying)
Could you please clue me in on how I can correctly achieve this? I would be sooo happy to get rid of all the repetitive code, folders and files I have! - Thanks!
Either you pass it directly into each method call:
public function foo($this_id) {
$this->bar($this_id);
}
Or you make it a class attribute, and simply ACCESS it from the various methods:
public function foo($this_id) {
$this->id = $this_id;
$this->bar();
}
public function bar() {
do_something($this->id);
}

Laravel / Eloquent eager loading

This will be an easy question for some, but I can't seem to find the answer online or in the documentation (only variants of it which I don't want).
Lets say we have a Question class
Each Question object can optionally have multiple Tags
I have set the classes up and things behave as expected. Currently I use:
$questions = Question::all();
This works as expected, however it does not eager load.
To be clear: $question->tags gives the array I am expecting. There is not a relationship problem.
Due to new requirements I will be creating a view with possibly thousands of questions, so this eager loading is a must.
I attempted to use:
$questions = Question::with('tag')->all();
Which gives error message:
BadMethodCallException in Builder.php line 2345: Call to undefined method Illuminate\Database\Query\Builder::all()
Every tutorial, or way I google eager loading, either specifies an ID, OR is a tutorial on how to ONLY show parents with children.
I simple want "all and their children".
This has got to be easy.. does anyone know how to do it?
Thanks
Rick
You should define the method in your model. As far as I see you're going to have one to many relationship. And that's going to be
class Question extends Model
{
public function tags()
{
return $this->hasMany('App\Tag');
}
}
Tag class
class Tag extends Model
{
public function question()
{
return $this->belongsTo('App\Question');
}
}
And the controller. Instead of all() use get() method.
$questions = Question::with('tags')->get();
As I defined tags method in the Question model. Question::with('tags') should call it. Instead, if you're going to do Question::with('tag'), tag method should be defined in Question model.
Notice the s
I've marked both answers as correct: the original syntax suggested is correct, and the class name issue raised was the final issue.
Thanks all:
$questions = Question::with('tags')->get();

Can I use a function as the name of a function?

I am modifying my PHP framework and trying to figure out an easier way to deal with different request types.
Currently I have this block in a controller method
$methodHandler = self::getMethodHandler(__FUNCTION__);
$this->$methodHandler();
Where getMethodHandler is
protected static function getMethodHandler($function) {
return $function."_".ucwords(strtolower(Request::getMethod()));
}
Ideally I want to reduce that two lines into one but PHP ain't having it
$this->self::getMethodHandler(__FUNCTION__)();
Anyway I could do this?
This should work:
$this->{self::getMethodHandler(__FUNCTION__)}();
That will evaluate self::getMethodHandler(__FUNCTION__) and call the result as a method of $this.

php static functions vs instance functions, basics

I'm trying to learn when static functions should be used, and have had a difficult time finding an answer my questions. I am creating a class User, which is related to a class Group. If I have a user id and I want to get a user object from that, is it better to do something like
$existingUser = User::get($userId);
where the class is defined like this
class User()
{
public static function get($id){
$user = new User();
return $user->findById($id);
}
public function findById($id) {
//find and populate user object
}
}
or
$existingUser=new User();
$existingUser->findById($userId);
where the class is defined like this
class User()
{
public function findById($id) {
//find and populate user object
}
}
What about if I were to write a function which returns an array of Group objects based on a user id?
class User()
{
//stuff
$groupArray = Group::getAllByUserId($this->getId())
//stuff
}
or
class User()
{
//stuff
$group = new Group();
$groupArray = $group->findAllByUserId($this->getId());
//stuff
}
The second method creates an empty group object which is never used. Does it matter?
Am I misunderstanding the concept of static? I know it is useful for not having to instantiate a class, so if the function instantiates one anyway, does that kind of defeat the purpose? If so, what would be an example of when a static function would be used?
Anything else I should be considering in this over simplified example?
You don't need a static function int he case you show above.
Static functions are really just global functions with a namespace.
Use them when the global state of the application needs to be controlled, or if multiple copies of the function lead to inonsistant results.
Callbacks sometimes need to be static, especially if they are passed as a string.
I'm trying to learn when static functions should be used
Oh, it's so simple: never.
To understand it, read:
http://www.objectmentor.com/resources/articles/ocp.pdf
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
I find a good rule of thumb is thinking "If I don't have a [class-name], would I expect to be able to call [method-name]?"
If I don't have a user, would I expect to be able to call findByID?
Probably not. This is one of the exceptions I come across; a "load" or a "save" method sometimes makes sense to be static.
A perfect example of when to use non-static methods is (most methods in) a Database class - you should always have a database object before you try to run a query on it.
An example of when to use a static method would be a "helper" class, essentially a collection of handy functions. Say you have some methods that help you output HTML, you might have HTML::image(), HTML::url() and HTML::script(). On these, you shouldn't need a HTML object to create an image, URL, and so on.
As for stopping multiple copies of objects being created (one argument for using static methods), you should use a Singleton pattern instead (Google it) to ensure only one copy of the object ever exists.
You should probably check out this question on Active Record vs data mapper:
https://stackoverflow.com/questions/2169832/data-mapper-vs-active-record
One take from this question is that static methods on the class for loading/saving aren't really the core functionality of the class in most cases. Further, storing and loading is a kind of abstract concept that is separate from your class objects in most cases.
Isa "user" a data storage and retrieval object? In most cases, no, it is a person represented in your system that has various properties and functions. When you start tying the persistence of that object into the object, you break encapsulation and make it harder to maintain the code. What if next week you want to load your users out of memcache? It's hardly relevant to if a user can have some property or functionality.

Categories