I'm building a CMS using Laravel 4 and I have a base admin controller for the admin pages that looks something like this:
class AdminController extends BaseController {
public function __construct(UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module)
{
$this->auth = $auth;
$this->user = $this->auth->adminLoggedIn();
$this->message = $message;
$this->module = $module;
}
}
Im using Laravel's IOC container to inject the class dependencies into the constructor. I then have various controller classes that control the different modules that make up the CMS and each class extends the admin class. For example:
class UsersController extends AdminController {
public function home()
{
if (!$this->user)
{
return Redirect::route('admin.login');
}
$messages = $this->message->getMessages();
return View::make('users::home', compact('messages'));
}
}
Now this works perfectly however my problem, which is less of a problem and more of a efficiency issue, occurs when I add a constructor to the UsersController class. For example:
class UsersController extends AdminController {
public function __construct(UsersManager $user)
{
$this->users = $users;
}
public function home()
{
if (!$this->user)
{
return Redirect::route('admin.login');
}
$messages = $this->message->getMessages();
return View::make('users::home', compact('messages'));
}
}
Since the child class now has a constructor it means the parent's constructor isn't getting called and thus things the child class is dependant on, such as this->user are no longer valid, causing errors. I can call the admin controller's construct function via parent::__construct() however since I need to pass it the class dependencies I need to set these dependencies in the child constructor resulting in something that looks like this:
class UsersController extends AdminController {
public function __construct(UsersManager $user, UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module)
{
parent::__construct($auth, $messages, $module);
$this->users = $users;
}
// Same as before
}
Now this works fine in terms of its functionality; however it doesn't seem very efficient to me to have to include the parent's dependencies in every child class that has a constructor. It also looks quite messy. Does Laravel provide a way around this, or does PHP support a way of calling both the parent and child constructor without having to call parent::__construct() from the child?
I know this is a long question for what is effectively not a problem but more me just be ocd about efficiency, but I appreciate any ideas and/or solutions.
Thanks in advance!
There isn't a perfect solution and it's important to understand that this isn't an issue with Laravel itself.
To manage this, you can do one of three things:
Pass the necessary dependencies to the parent (which was your issue)
// Parent
public function __construct(UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module)
{
$this->auth = $auth;
$this->user = $this->auth->adminLoggedIn();
$this->message = $message;
$this->module = $module;
}
// Child
public function __construct(UsersManager $user, UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module)
{
$this->users = $users;
parent::__construct($auth, $message, $module);
}
Auto resolve the dependencies in the parent construct as stated by #piotr_cz in his answer
Create the instances in the parent construct instead of passing them as parameters (so you don't use Dependency Injection):
// Parent
public function __construct()
{
$this->auth = App::make('UserAuthInterface');
$this->user = $this->auth->adminLoggedIn();
$this->message = App::make('MessagesInterface');
$this->module = App::make('ModuleManagerInterface');
}
// Child
public function __construct(UsersManager $user)
{
$this->users = $users;
parent::__construct();
}
If you want to test your classes, the third solution will be more difficult to test. I'm unsure if you can mock the classes using the second solution, but you mock them using the first solution.
I know this is a super old question, but I just finished grinding on a similar question on my current project and came to an understanding with the issue at hand.
The basic underlying question here is:
If I'm extending a parent class that has a constructor. That constructor has injected dependancies and all of it's dependencies are already documented in the parent itself. Why do I have to include the parent's dependencies again in my child class?
I ran into this same issue.
My parent class requires 3 different dependencies. They're injected via the constructor:
<?php namespace CodeShare\Parser;
use CodeShare\Node\NodeRepositoryInterface as Node;
use CodeShare\Template\TemplateRepositoryInterface as Template;
use CodeShare\Placeholder\PlaceholderRepositoryInterface as Placeholder;
abstract class BaseParser {
protected $node;
protected $template;
protected $placeholder;
public function __construct(Node $node, Template $template, Placeholder $placeholder){
$this->node = $node;
$this->template = $template;
$this->placeholder = $placeholder;
}
The class is an abstract class so I can never instantiate it on it's own. When I extend the class, I still need to include all of those dependencies and their use references in the child's constructor:
<?php namespace CodeShare\Parser;
// Using these so that I can pass them into the parent constructor
use CodeShare\Node\NodeRepositoryInterface as Node;
use CodeShare\Template\TemplateRepositoryInterface as Template;
use CodeShare\Placeholder\PlaceholderRepositoryInterface as Placeholder;
use CodeShare\Parser\BaseParser;
// child class dependencies
use CodeShare\Parser\PlaceholderExtractionService as Extractor;
use CodeShare\Parser\TemplateFillerService as TemplateFiller;
class ParserService extends BaseParser implements ParserServiceInterface {
protected $extractor;
protected $templateFiller;
public function __construct(Node $node, Template $template, Placeholder $placeholder, Extractor $extractor, TemplateFiller $templateFiller){
$this->extractor = $extractor;
$this->templateFiller = $templateFiller;
parent::__construct($node, $template, $placeholder);
}
Including the use statements for the 3 parent dependencies in each class seemed like duplicate code since they're already defined in the parent constructor. My thought was to remove the parent use statements as they'll always need to be defined in the child class that extends the parent.
What I realized is that including the use for the dependencies in the parent class and including the class names in the parent's constructor are ONLY needed for type hinting in the parent.
If you remove the use statements from the parent and the type hinted class name from the parents constructor, you get:
<?php namespace CodeShare\Parser;
// use statements removed
abstract class BaseParser {
protected $node;
protected $template;
protected $placeholder;
// type hinting removed for the node, template, and placeholder classes
public function __construct($node, $template, $placeholder){
$this->node = $node;
$this->template = $template;
$this->placeholder = $placeholder;
}
Without the use statements and type hinting from the parent, it can no longer guarantee the type of class being passed to it's constructor because it has no way of knowing. You could construct from your child class with anything and the parent would accept it.
It does seem like double entry of code, but really in your paren't you're not constructing with the dependencies laid out in the parent, you're verifying that the child is sending in the correct types.
There's a way.
When BaseController autoresolves it's dependecies.
use Illuminate\Routing\Controller;
use Illuminate\Foundation\Application;
// Dependencies
use Illuminate\Auth\AuthManager;
use Prologue\Alerts\AlertsMessageBag;
class BaseController extends Controller {
protected $authManager;
protected $alerts;
public function __construct(
// Required for resolving
Application $app,
// Dependencies
AuthManager $authManager = null,
AlertsMessageBag $alerts = null
)
{
static $dependencies;
// Get parameters
if ($dependencies === null)
{
$reflector = new \ReflectionClass(__CLASS__);
$constructor = $reflector->getConstructor()
$dependencies = $constructor->getParameters();
}
foreach ($dependencies as $dependency)
{
// Process only omitted optional parameters
if (${$dependency->name} === null)
{
// Assign variable
${$dependency->name} = $app->make($dependency->getClass()->name);
}
}
$this->authManager = $authManager;
$this->alerts = $alerts;
// Test it
dd($authManager);
}
}
So in child controller you pass only Application instance:
class MyController extends BaseController {
public function __construct(
// Class dependencies resolved in BaseController
//..
// Application
Application $app
)
{
// Logic here
//..
// Invoke parent
parent::__construct($app);
}
}
Of course, we could use Facade for application
You must pass the dependencies to the parent constructor to have them available in the child. There is no way to inject the dependencies on the parent construct when you instantiate it via the child.
I came across the same issue, when extending my base Controller.
I opted for a different approach than the other solutions shown here. Rather than rely on dependency injection, I'm using app()->make() in the parents constructor.
class Controller
{
public function __construct()
{
$images = app()->make(Images::class);
}
}
There may be downsides to this simpler approach - possibly making the code less testable.
I've also ended up with this problem and cleared this mess by not calling constructor at child class and use extra needed dependencies inside the function parameters.
It will work with controllers because you won't need to call these functions manually and you can inject everything there. So common dependencies goes to the parent and less needed will be added to the methods itself.
Related
i have to test class add_Hook
class add_Hook extends Hooks_base
i tried to create a mock of hooks_base like this:
> $this->oHookBaseMock = $this->getMock(
> 'Hooks_base',
> array('get_database','get_arguments'),
> array(null,'null') //this are the parameters for the constructor
> );
$this->hook->expects($this->any())
->method('get_database')
->will( $this->returnValue(true)
);
$this->hook->expects($this->any())
->method('get_arguments')
->will( $this->returnValue($arraySImpleXmlObject)
);
and doing something like this :
$hook = new add_Hook($this->hook)
And now my problem is that when i run the test it steels ask me for the 2 parameters of the parent class (hooks_base)
the constructor is something like this
public function __construct($parameter_1, $parameter_2) {
$this->_param1 = $parameter_1;
$this->_param2 = $parameter_2;
}
i dont' know how to disable the constructor of the parent class and if i do it.
I simply create an instance of the abstract class in my test. $Object = new ABSTRACT_CLASS() and have no code in the definition of the methods that need defined as I do not intend to test the abstract method.
class TestAbstractInput extends AbstractInput
{
public ImplementAbstractFunction()
{ // Purposely empty so nothing happens. This would be defined in normal class extending AbstractInput
}
...
}
class AbstractInputTest extends TestCase
{
// ...
public function setUp()
{
$this->TestClassObject = new TestAbstractInput();
}
public function test_isValid()
{
$this->assertEquals(1, $this->TestClassObject->isValid);
}
}
This way the individual methods may be tested. Then I test the normal class that usually extends the abstract class to test the functions that were implemented in that class.
class InputTest extends TestCase
{
// ...
public function setUp()
{
$this->TestClassObject = new AbstractInput();
}
// Parent function should still work
public function test_isValid()
{
$this->assertEquals(1, $this->TestClassObject->isValid);
}
public function test_ImplementAbstractFunction()
{
$this->assertTrue($this->AbstractFunction());
...
}
}
EDIT There is also the built in getMockForAbstractClass() which may work directly for you if you are using a new enough version. You can also instruct Mocks to not run the constructor.
You can't "mock" the parent class of your class. You could create a mock of your class and replace the parent methods that you are wanting to replace.
$add_hook = $this->getMock('add_Hook',
//This will mock these methods only and leave the rest untouched provided the class is able to be loaded.
array('get_database','get_arguments'),
array(param1, param2));
$this->hook->expects($this->any())
->method('get_database')
->will( $this->returnValue(true)
);
$this->hook->expects($this->any())
->method('get_arguments')
->will( $this->returnValue($arraySImpleXmlObject)
);
Your attempt was to somehow inject the parent into class. The class is an instance of the parent so you can't "mock" it without mocking the class itself.
In my opinion, this is not a good practice. You are testing the implementation of your class rather than testing the behavior of your class. Your test should rather check what your method does rather than how it does it. What is that 'get_database' and 'get_arguments' do? Perhaps you should be passing an instance of 'Hooks_base' into your class rather than extending it.
From what you have posted my test would look like:
public function testInit() {
$param1 = <what ever the argument needs to be>;
$param2 = <something else>;
$addHook = new add_Hook($param1, $param2);
$this->assertInstanceOf('Hooks_base', $addHook); //I would probably have this in a constructor test
$addHook->init();
//Assertions for what init() does
}
Take the following code as an example of what i want:
class SomethingController extends Factory
{
private $somethingRepository;
public function __Construct( ISomethingRepository $repo )
{
$this->somethingRepository = $repo;
}
}
class Factory
{
public function __Construct()
{
// The following call to AddBinding would push into SomethingController the new instance of the class denoted in my AddBinding second parameter.
$this->AddBinding( ISomethingRepository, MySQLSomethingRepository);
// So in this case, if the controller i'm extending has a construct parameter of ISomethingRepository, then make the parameter equal a new MySQLSomethingRepository()
// Then if I want to use JSONSomethingRepository in the future, I only have to change the one AddBinding call and the controller will still work.
}
public function AddBinding( $interface, $concrete )
{
// Somehow assign the constructor properties of the extending class as new instances of the bindings i have called upon in the construct of my factory class (see this class's construct)
// Pseudo code:
// ----------------------
$calledClass = get_called_class();
$class = new \ReflectionClass( $calledClass );
$method = $class->getMethod( "__construct" );
$params = $method->getParameters();
foreach( $params as $param )
{
if ( $param == $interface )
{
return new $concrete;
}
}
// /Pseudo code:
// ----------------------
}
}
I want to implement a factory kind of class.
This factory class will be extended by a controller class.
the factory class will look at the construct parameters of the controller class and make new instances of the object based off of my AddBindings method in the factory.
Let's say I wanted to have a MySQLSomethingRepository which has data coming from MySQL... injected into my SomethingController... Somewhere I need to declare that
SomethingController( new MySQLSomethingRepository() )...
which hopefully will be dealt with by my factory class...
The current way i'm doing it is that is forcing a direct coupling with the data source... which is making it very hard to do test cases with:
private $somethingRepository = new MySQLSomethingRepository();
so imagine if i have used this same repository in loads of other controllers and i want to change my database source to some json data and i implement the following repository "JsonSomethingRepository", I have to go and change all of the controllers to:
private $somethingRepository = new JsonSomethingRepository();
How might i implement my Factory class so that it can deal with creating the instances my controller class is demanding inside the AddBindings function?
Design an abstract class in Adapter model and provide some common methods for child class.
You can design both repos with adapters to injected in you controllers.
My recommendtation is to use Abstract class and do it in way below:
class SomethingController extends AbstractController {
}
abstract class AbstractController {
protected $somethingRepository;
public function __Construct(ISomethingRepository $repo) {
$this->somethingRepository = $repo;
$this->AddBinding ( ISomethingRepository, MySQLSomethingRepository );
}
public function AddBinding($interface, $concrete) {
// Somehow assign the constructor properties of the extending class as new instances of the bindings i have called upon in the construct of my factory class (see this class's construct)
}
}
Hope it would be help.
I'm building a User Class for my new website, however this time I was thinking to build it little bit differently...
C++, Java and even Ruby (and probably other programming languages) are allowing the use of nested/inner classes inside the main class, which allows us to make the code more object-oriented and organized.
In PHP, I would like to do something like so:
<?php
public class User {
public $userid;
public $username;
private $password;
public class UserProfile {
// some code here
}
private class UserHistory {
// some code here
}
}
?>
Is that possible in PHP? How can I achieve it?
UPDATE
If it's impossible, will future PHP versions might support nested classes?
Intro:
Nested classes relate to other classes a little differently than outer classes. Taking Java as an example:
Non-static nested classes have access to other members of the enclosing class, even if they are declared private. Also, non-static nested classes require an instance of the parent class to be instantiated.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
There are several compelling reasons for using them:
It is a way of logically grouping classes that are only used in one place.
If a class is useful to only one other class, then it is logical to
relate and embed it in that class and keep the two together.
It increases encapsulation.
Consider two top-level classes, A and B, where B needs access to
members of A that would otherwise be declared private. By hiding class
B within class A, A's members can be declared private and B can access
them. In addition, B itself can be hidden from the outside world.
Nested classes can lead to more readable and maintainable code.
A nested class usually relates to it's parent class and together form a "package"
In PHP
You can have similar behavior in PHP without nested classes.
If all you want to achieve is structure/organization, as Package.OuterClass.InnerClass, PHP namespaces might sufice. You can even declare more than one namespace in the same file (although, due to standard autoloading features, that might not be advisable).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
If you desire to emulate other characteristics, such as member visibility, it takes a little more effort.
Defining the "package" class
namespace {
class Package {
/* protect constructor so that objects can't be instantiated from outside
* Since all classes inherit from Package class, they can instantiate eachother
* simulating protected InnerClasses
*/
protected function __construct() {}
/* This magic method is called everytime an inaccessible method is called
* (either by visibility contrains or it doesn't exist)
* Here we are simulating shared protected methods across "package" classes
* This method is inherited by all child classes of Package
*/
public function __call($method, $args) {
//class name
$class = get_class($this);
/* we check if a method exists, if not we throw an exception
* similar to the default error
*/
if (method_exists($this, $method)) {
/* The method exists so now we want to know if the
* caller is a child of our Package class. If not we throw an exception
* Note: This is a kind of a dirty way of finding out who's
* calling the method by using debug_backtrace and reflection
*/
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
if (isset($trace[2])) {
$ref = new ReflectionClass($trace[2]['class']);
if ($ref->isSubclassOf(__CLASS__)) {
return $this->$method($args);
}
}
throw new \Exception("Call to private method $class::$method()");
} else {
throw new \Exception("Call to undefined method $class::$method()");
}
}
}
}
Use case
namespace Package {
class MyParent extends \Package {
public $publicChild;
protected $protectedChild;
public function __construct() {
//instantiate public child inside parent
$this->publicChild = new \Package\MyParent\PublicChild();
//instantiate protected child inside parent
$this->protectedChild = new \Package\MyParent\ProtectedChild();
}
public function test() {
echo "Call from parent -> ";
$this->publicChild->protectedMethod();
$this->protectedChild->protectedMethod();
echo "<br>Siblings<br>";
$this->publicChild->callSibling($this->protectedChild);
}
}
}
namespace Package\MyParent
{
class PublicChild extends \Package {
//Makes the constructor public, hence callable from outside
public function __construct() {}
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
class ProtectedChild extends \Package {
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
}
Testing
$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)
Output:
Call from parent -> I'm Package protected method
I'm Package protected method
Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context
NOTE:
I really don't think trying to emulate innerClasses in PHP is such a good idea. I think the code is less clean and readable. Also, there are probably other ways to achieve similar results using a well established pattern such as the Observer, Decorator ou COmposition Pattern. Sometimes, even simple inheritance is sufficient.
Real nested classes with public/protected/private accessibility were proposed in 2013 for PHP 5.6 as an RFC but did not make it (No voting yet, no update since 2013 - as of 2021/02/03):
https://wiki.php.net/rfc/nested_classes
class foo {
public class bar {
}
}
At least, anonymous classes made it into PHP 7
https://wiki.php.net/rfc/anonymous_classes
From this RFC page:
Future Scope
The changes made by this patch mean named nested classes are easier to implement (by a tiny bit).
So, we might get nested classes in some future version, but it's not decided yet.
You cannot do this in PHP. However, there are functional ways to accomplish this.
For more details please check this post:
How to do a PHP nested class or nested methods?
This way of implementation is called fluent interface: http://en.wikipedia.org/wiki/Fluent_interface
As per Xenon's comment to Anıl Özselgin's answer, anonymous classes have been implemented in PHP 7.0, which is as close to nested classes as you'll get right now. Here are the relevant RFCs:
Nested Classes (status: withdrawn)
Anonymous Classes (status: implemented in PHP 7.0)
An example to the original post, this is what your code would look like:
<?php
public class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
$this->profile = new class {
// Some code here for user profile
}
$this->history = new class {
// Some code here for user history
}
}
}
?>
This, though, comes with a very nasty caveat. If you use an IDE such as PHPStorm or NetBeans, and then add a method like this to the User class:
public function foo() {
$this->profile->...
}
...bye bye auto-completion. This is the case even if you code to interfaces (the I in SOLID), using a pattern like this:
<?php
public class User {
public $profile;
public function __construct() {
$this->profile = new class implements UserProfileInterface {
// Some code here for user profile
}
}
}
?>
Unless your only calls to $this->profile are from the __construct() method (or whatever method $this->profile is defined in) then you won't get any sort of type hinting. Your property is essentially "hidden" to your IDE, making life very hard if you rely on your IDE for auto-completion, code smell sniffing, and refactoring.
Since PHP version 5.4 you can force create objects with private constructor through reflection. It can be used to simulate Java nested classes. Example code:
class OuterClass {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function forkInnerObject($name) {
$class = new ReflectionClass('InnerClass');
$constructor = $class->getConstructor();
$constructor->setAccessible(true);
$innerObject = $class->newInstanceWithoutConstructor(); // This method appeared in PHP 5.4
$constructor->invoke($innerObject, $this, $name);
return $innerObject;
}
}
class InnerClass {
private $parentObject;
private $name;
private function __construct(OuterClass $parentObject, $name) {
$this->parentObject = $parentObject;
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function getParent() {
return $this->parentObject;
}
}
$outerObject = new OuterClass('This is an outer object');
//$innerObject = new InnerClass($outerObject, 'You cannot do it');
$innerObject = $outerObject->forkInnerObject('This is an inner object');
echo $innerObject->getName() . "\n";
echo $innerObject->getParent()->getName() . "\n";
You can't do it in PHP. PHP supports "include", but you can't even do that inside of a class definition. Not a lot of great options here.
This doesn't answer your question directly, but you may be interested in "Namespaces", a terribly ugly\syntax\hacked\on\top\of PHP OOP:
http://www.php.net/manual/en/language.namespaces.rationale.php
I think I wrote an elegant solution to this problem by using namespaces. In my case, the inner class does not need to know his parent class (like the static inner class in Java). As an example I made a class called 'User' and a subclass called 'Type', used as a reference for the user types (ADMIN, OTHERS) in my example. Regards.
User.php (User class file)
<?php
namespace
{
class User
{
private $type;
public function getType(){ return $this->type;}
public function setType($type){ $this->type = $type;}
}
}
namespace User
{
class Type
{
const ADMIN = 0;
const OTHERS = 1;
}
}
?>
Using.php (An example of how to call the 'subclass')
<?php
require_once("User.php");
//calling a subclass reference:
echo "Value of user type Admin: ".User\Type::ADMIN;
?>
You can, like this, in PHP 7:
class User{
public $id;
public $name;
public $password;
public $Profile;
public $History; /* (optional declaration, if it isn't public) */
public function __construct($id,$name,$password){
$this->id=$id;
$this->name=$name;
$this->name=$name;
$this->Profile=(object)[
'get'=>function(){
return 'Name: '.$this->name.''.(($this->History->get)());
}
];
$this->History=(object)[
'get'=>function(){
return ' History: '.(($this->History->track)());
}
,'track'=>function(){
return (lcg_value()>0.5?'good':'bad');
}
];
}
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();
It is waiting for voting as RFC
https://wiki.php.net/rfc/anonymous_classes
This page keeps coming up in my Internet searches on this subject so figured I should chime in even though this is an 8-year old post. The documentation for PHP5 demonstrates that anonymous classes can be defined within a class method. The object created can extend, implement, and even use other classes, interfaces, and traits. Consider the following OOP paradigm of factory object production. Similar to what #e-i-pi pointed out ...
class Factory {
/**
* Method to manufacture an inner-class object.
*
* #param string $args Arguments to be passed to
* the inner-class constructor.
*/
static function manufacture_object($args) {
/**
* Here's the definition of the inner-class.
*/
return new class($args) {
static $remembers = 'Nothing';
private $args;
function __construct($args) {
$this->$args = $args;
}
function says() {
return $this->args;
}
};
}
}
/**
* Create an inner-class object and have it do its thing.
*/
$mort = Factory::manufacture_object("Hello World!");
echo $mort->says(); // Echoes "Hello World!"
The objects are one-off, so one would expect the static values of the objects returned would not bind from one instance to another. After all, the anonymous class is unique from one object to another. However, late static binding works as one would otherwise expect from a nested class.
$mort = Factory::manufacture_object("I can remember that.");
$mort2 = Factory::manufacture_object("I'll live vicariously through you.");
$mort::$remembers = 'Something';
echo $mort2::$remembers; // Echoes "Something"
So, there you go: inner/nested classes and creation of their objects with static functionality has been possible since September 22, 2013 (right about the time this question was asked).
Put each class into separate files and "require" them.
User.php
<?php
class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
require_once('UserProfile.php');
require_once('UserHistory.php');
$this->profile = new UserProfile();
$this->history = new UserHistory();
}
}
?>
UserProfile.php
<?php
class UserProfile
{
// Some code here
}
?>
UserHistory.php
<?php
class UserHistory
{
// Some code here
}
?>
I'm struggling to get my Behavior class to use an object instance in the callbacks.
class SomethingBehavior extends ModelBehavior
{
public function setObject($obj)
{
// do stuff
}
public function afterFind(Model $model,$results,$primary)
{
// use the $obj reference set above
}
}
Now I need the Model class to call setObject(..) before any find operations are performed. So ideally I would just assign the object I need in the constructor.
class Document extends AppModel
{
//.....
public function __construct($id,$table,$ids)
{
parent::__construct($id,$table,$ds);
$this->Something->setObject(new MyClass());
}
}
My problem is that the Behavior object isn't yet configured, and I get a not an object error when trying to use it.
I can't find any callback method for Models like in Components. For example, there is no setup or initialize method.
How can I assign the object I need to the Behavior?
You don't seem to have worked with behaviors much. Try to use the containable, tree or other core or plugin behaviors, then you will soon figure out the basics.
First of all, behaviors are attached to models (and since 2.3: loaded), not the other way around. A model then gets "richer" in functionality.
Either statically be using public $actsAs or dynamically using
$this->Behaviors->attach('Something'); // since 2.3: load() instead of attach()
It can directly access the behavior methods. Lets say we have a method foo() in your behavior.
You can then call it from your model as
$this->foo($foo, $bar);
Or from your controller as
$this->Document->Behaviors->attach('Something')
$this->Document->foo($foo, $bar);
Awesome, right?
The behavior method usually has this declaration:
public function foo(Model $Model, $foo, $bar) {
$alias = $Model->alias;
// do sth
}
As you can see, you always pass the model into it implicitly (as first argument automatically passed).
You can access all its attributes.
And do not touch the constructor of the model. no need to do that.
If you really need to pass an object in at runtime, why does your approach not work?
public function setObject(MyClass $obj) {
$this->Obj = $obj;
}
Now you can internally use the object from your behavior methods
public function doSth(Model $Model) {
$this->Obj->xyz();
}
Also this might not be the most elegant approach.
You never set the something member of the Document class. You either need to instantiate it inside the constructor, or pass it in.
Personally, I would do something like this:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
$this->behavior->setObject(new MyClass());
}
}
$doc = new Document(..., new SomethingBehavior());
Or better yet, you could even separate it further by doing:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
}
}
$behavior = new SomethingBehavior();
$behavior->setObject(new MyClass());
$doc = new Document(..., $behavior);
That way, there is less magic going on in the constructor.
I've seen a few questions with really similar titles but they where irrelevant to my specific problem.
Basically, I want to access the variables from my core class in a class which extends core, but things seem to be quite complicated compared to other examples. I am using a MVC framework. I've simplified the code below to remove anything that was irrelevant.
index.php
// Load the core
include_once('core.php');
$core = new Core($uri, $curpath);
$core->loadController('property');
core.php
class Core
{
public $uri;
public $curpath;
function __construct($uri, $curpath)
{
$this->uri = $uri;
$this->curpath = $curpath;
}
// Load the controller based on the URL
function loadController($name)
{
//Instantiate the controller
require_once('controller/'.$name.'.php');
$controller = new $name();
}
}
property.php
class Property extends Core
{
function __construct()
{
print $this->curpath;
}
}
Printing $this->curpath just returns nothing. The variable has been set but it is empty.
If I print $this->curpath inside core.php it prints fine.
How can I access this variable?
You are doing it wrong tm
You should be utilizing an autoloader, instead of including files with each class manually. You should learn about spl_autoload_register() and and namespaces, and how to utilize both of them.
Do not generate output in the __construct() methods. That's an extremely bad practice
The variables are still there. That is not the problem. In PHP, when you extend a class, it does not inherit the constructor.
You do not understand how inheritance works. When you call method on instance of extended class it will not execute parent class's method , before calling extended class's methods. They get overwritten , not stacked.
Object variables should not be exposed. You are breaking the encapsulation. Instead og defining them as public you should use protected.
You should extend classes of they are different type same general thing. The extends in PHP means is-a. Which means that, when you write class Oak extends Tree, you mean that all the oaks are trees. The same rule would mean, that in your understanding all Property instances are just a special case of Core instances. Which they clearly ain't.
In OOP, we have principle. One of which is Liskov substitution principle (shorter explanation). And this is the thing your classes are violating.
The problem, I think, lies here:
If you consider a simple inheritance like this one:
class Dog{
public $color;
public function __construct($color){
$this->color = $color;
}
}
class TrainedDog extends Dog{
public $tricks;
public function __construct($color, $tricks){
$this->tricks = $tricks;
parent::__construct($color);
}
}
//Create Dog:
$alfred = new Dog('brown');
//Create TrainedDog
$lassie = new TrainedDog('golden',array('fetch'));
In this example $alfred is a brown dog and $lassie is a golden dog. The two instances are separate from each other, the only thing they have in common is that they both have a property called $color.
If you want a variable that is available in all Dogs for example, you need a class variable:
class Dog{
public $color;
public static $numberOfLegs; //Class variable available in every instance of Dog.
public function __construct($color, $numberOfLegs){
$this->color = $color;
self::$numberOfLegs = $numberOfLegs;
}
}
class TrainedDog extends Dog{
public $tricks;
public function __construct($color, $tricks){
$this->tricks = $tricks;
parent::__construct($color);
echo parent::$numberOfLegs;
}
}
This does not make much sense in many cases though, because if you have two instances of the parent class (in you're case Core), they also share the class variable.
Unless you can ensure that Core is instanciated only once, this approach will not work. If it does only exist once, you can just as well use constant variables to store the 2 properties.
If there exist multiple instances/objects of Core, I'd recommend using a composition (as suggested by Alvin Wong).
class Core{
//Just as you programmed it.
}
class Property{
private $core;
public function __construct($core){
$this->core = $core;
echo $core->curPath;
}
}
Try this
include_once('core.php');
$core = new Core('test', 'path');
$core->loadController('property');
class Property extends Core
{
function __construct($date)
{
print $date->curpath;
}
}
class Core
{
public $uri;
public $curpath;
function __construct($uri, $curpath)
{
$this->uri = $uri;
$this->curpath = $curpath;
}
// Load the controller based on the URL
function loadController($name)
{
//Instantiate the controller
require_once($name.'.php');
$controller = new $name($this);
}
}