I am using an ORM class - each table in the DB is represented using a subclass of the ORM class.
I am using PHP interfaces, and I wish to specify which methods (db fields) are required in some of my ORM subclasses. Adding a function to an interface requires the method to be explicitly declared in the class. However, these methods rely on magic methods for the actual functionality as the DB structure is unknown to the ORM before run time.
What I imagined doing was creating functions for each, which would return a result from the parent class.
Consider:
class ORM
{
// Library code here. Can't change this.
public function __call($name, $arguments)
{
return call_user_func_array(array($this, $method), $arguments);
}
}
interface MyTableInterface
{
public function myDbField();
}
class MyTable extends ORM implements MyTableInterface
{
public function myDbField()
{
return parent::myDbField();
}
}
With this code, when I call parent::myDbField() from the MyTable class, it correctly moves to the ORM class and uses the __call magic method. Once here, $this equals MyTable and it calls the original function from the MyTable class instead of initiating it's own logic to pull info from the DB.
How can I avoid this recursion?
One way to do it could be passing $orm instance as a dep (constructor, setter or whatever logic). Ex:
class MyTable {
protected $orm;
public function __construct(Orm $orm)
{
$this->orm = $orm;
}
public function myDbField()
{
return $this->orm->myDbField();
}
}
This way, $this inside Orm __call refers to Orm instance. Perhaps, this could be a scenario to use adapter pattern?
call method is only invoked when there is no method found, in your example it is not possible because the method is found in the subclass
Related
With, where, hasMany etc. methods of Eloquent class are not static.
However we call these functions like that:
// Post is a child of Model class.
Post::where(...); // we don't use New keyword.
So, does Laravel Framework initiate all Model instances before we call their methods?
In the Eloquent's Model class, you have the below function which handles the static method calls dynamically.
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
As you can see, it creates an instance of the Model class on which a non static method is invoked statically, then invokes that method on the instance.
I currently have an abstract class which i am extending to other controllers. I have a abstract function within the abstract class which takes the value and places it in the __construct.
abstract class Controller extends BaseController {
abstract public function something();
public function __construct(Request $request) {
if (!is_null($this->something())){
$this->global_constructor_usse = $this->something();
}
}
}
My problem is that, on controllers that don't require this abstract function, I am having to place in the empty function.
class ControllerExample extends Controller {
public function something(){
return 'somethinghere';
}
}
Is there anyway to making the abstract function optional, or have a default value?
class EmptyControllerExample extends Controller {
public function something(){}
}
It is not possible to have a abstract method optional, as it is implied in PHP that all abstract methods must have an implementation.
There are legit use cases for optional abstract methods, yes: event handlers, metadata describers, etc. Unfortunately, you'll need to use regular, non-abstract methods with an empty body, and indicate in PHPDoc that they will do nothing unless extended.
Be wary, though: this can very quickly turn into code smell by diffusing a class responsability with their children. If you're dealing with generic events, you can look into Laravel's own event system, or the Observer pattern instead.
Abstract functions in a parent class, should only be used if its required by your application to implement the following method in all controllers who inherits from it, clearly it is not the case.
In this case i would make a trait. Here you create a trait which can be implemented by the classes who needs it. Notice the use keyword usage, use somethingTrait;
trait SomethingTrait
{
public function something()
{
echo "something called";
}
}
class Controller
{
use SomethingTrait;
public function run()
{
$this->something();
}
}
phpfiddle link
Another aproach could be doing a class inheritance structure, if the controllers you want to implement the methods has something in common. Where you would implement your special method in CrmController, where you still would be able to create shared methods in the abstract controller.
AbstractController
|
CrmController
|
CompanyController
For your question, 'Is there anyway to making the abstract function optional or have a default value?' No, and you are down the wrong path if you are trying to make abstract function optional. Hope my suggestions can help.
I've built a "model" superclass for a MVC framework. In most methods i do need only the class name so i've used get_called_class() but for save and edit methods i need to pass the object with values.
In my design, when you create a model object and you save or edit you have to do:
$object->save($object); or $object->update($object, $id).
I really don't like this, because looks as a bad design. I would like just to say:
$object->save(); and $object->update($id);
Since you are effectively saving the current object.
Models classes extends a Model parent that defines their behaviour and create the DB connection for them.
The methods of superclass that i would like to make does not take as an argument $object but rather i would like to say "get the calling object".
public function save($object) {
return self::$db->save($object);
}
public function update($object,$id) {
return self::$db->update($object, $id);
}
I know that this can be easily doable the in the object model with
public function save () {
parent::save($this);
}
But i would like not to have to reimplement this behaviour for every single model!
Thank you in advance.
The use of any existing ORM library isn't discussed here, since i want to provide a querybuilder and simple ORM that is PDO based as default. Because i do not want to have any 3rd party dependency as default
Define your base model as abstract class and inherit default behavior to child model classes.
abstract class Model
{
public function save() {
return self::$db->save($this);
}
public function update($id) {
return self::$db->update($this, $id);
}
}
class UserModel extends Model;
$myModel = new UserModel();
$myModel->save();
Normally Eloquent model is used as following:
class Article extends Eloquent
{
// Eloquent Article implementation
}
class MyController extends BaseController
{
public function getIndex()
{
$articles = Article::all(); // call static method
return View::make('articles.index')->with('articles', $articles);
}
}
But when restructing use Dependency Injection, it looks like that:
interface IArticleRepository
{
public function all();
}
class EloquentArticleRepository implements IArticleRepository
{
public function __construct(Eloquent $article)
{
$this->article = $article;
}
public function all()
{
return $this->article->all(); // call instance method
}
}
So why we can call the static method Article::all() in form of instance method $this->article->all()?
P/S: Sorry for my bad English.
Good question.
Laravel utilize the Facade design pattern. when you call Article::all(), a lot of things happened behind the screen. First, PHP try to call the static method if it fails php immediately call a magic method _callStatic. then Laravel cleverly capture the static call and create instance of the original class.
From Laravel doc:
Facades provide a "static" interface to classes that are available in the application's IoC container. Laravel ships with many facades, and you have probably been using them without even knowing it!
More info:
http://laravel.com/docs/facades
http://usman.it/laravel-4-uses-static-not-true/
I have an interface that declares the implementation needs methods such as find, findOrFail etc, basically Laravel eloquent methods.
I declare these methods in the interface because not everything that implements the interface will extend eloquent so I declare them in the interface so my app always knows the methods are going to be there.
What I want to know is, other than having a bunch of public function find($id){return parent::find($id)} type methods in the models that do extend the eloquent model is there an easy way to let the interface know that the method is handled via __call?
Although there may be a larger question as to the cleanliness of such a design, you can accomplish something akin to this by using a trait which implements the methods of the interface:
interface FindableContract {
public function find($id);
}
trait MagicFindableTrait {
public function find($id) {
return static::__call(__FUNCTION__, func_get_args());
}
}
class MagicalParent {
public function __call($method, $args) {
if ($method == 'find') {
return "User " . $args[0] . " is a witch! May we burn her?!";
}
}
}
class User extends MagicalParent implements FindableContract {
use MagicFindableTrait;
}
class NonmagicalUser implements FindableContract {
public function find($id) {
return "User $id was found to be non-magical. Let's burn him anyway.";
}
}
print (new User)->find(123);
print (new NonmagicalUser)->find(321);
No this will not work. While __call() is really nice for a dynamic coding style it's disadvantages are that you cannot enforce the signatures of the dynamic methods in an interface and you won't get an automated documentation for it.
But I think if you are at a point where you want to create an interface for those methods, there should be no need to use __call() anymore. I would just hardcode the methods.