I'll try to explain my situation as clear as I can.
I'm building a framework. Its system contains following directories:
1) Core - routing, main class (autoloading, configs, constants), requests
2) Helpers
3) Libraries
In index.php, I set all most important configuration variables such as the base url, or index file. I set them inside of the "main class".
These attributes are private, because I dont want anyone to change them during the request flow.
So the only point of access to these variables is from the "main class" methods.
Now, one of these private variables is $_base_url.
I want users to be able to access base_url from some better place however, like URL class (inside of the helper directory).
To achieve this, I have to create two methods - get_base_url() inside of the "main class", and get_base_url() inside of the url class, which will do something like this:
class Url {
public static function get_base_url() {
return Main_Class::get_base_url();
}
}
My problem is - I have a couple of such variables which I'd like to call from different classes.
I will have a lot of repeated code.
The only solution I see is just setting these variables in different classes directly, but I'd like to have them centralized inside the main class.
How should I handle this?
You can create another class called variables (or whatever you prefer) then use static variables with that clase to be able to call them back and forth. Something like this:
<?php class Variables {
static $_base_url = "http://base_url";
} ?>
That way you can call your variables like this:
Variables::$_base_url;
To expand on your comment:
With inheritance you can have a situation where 2 classes have different values for the same $variable. In this example all instances will hold a different value for $_base_url;
class ParentClass {
protected $_base_url = "SOME VALUE";
}
class SubClassA extends ParentClass {
protected __construct() {
$this->_base_url = 'VALUE X';
}
}
class SubClassB extends ParentClass {
protected __construct() {
$this->_base_url = 'DIFFERENT VALUE THAN SubClassA';
}
}
Again, it depends on what you are trying to accomplish. If you want your variables act more like Settings or if you just want to have those variables within your instance.
Why you need a variable to be called from the classname, if the classes extends your MainClass then use parent::get_base_url();
Related
I have a project that has quite a lot of classes, stored as separate files, and some of them inherit the same parent class. The class in question is a settings class, it holds user preferences and such.
I am wondering: What is the correct way to have these classes use the same information?
For example...
Do I use extends each time? Or does extends execute and redefine code each time?
Do I instantiate the classes in new variables, like $this->example = new Class();, in each of the inheriting classes __construct() function? Or does that use more memory?
Do I somehow instantiate the classes in new variables in a different class and pass the variables to the inheriting classes via function parameters? Or is that bad form?
I just don't know!
settings.php looks like this:
class Settings
{
public $pref = array();
function __construct() {
$this->pref['name'] = 'John';
$this->pref['age'] = 21;
$this->pref['display_dob'] = true;
...
}
}
The inheriting classes look like this:
class ShowPerson extends Settings
{
public function display()
{
echo $this->pref['name'], ' ';
echo $this->pref['age'], ' years old';
if ($this->pref['display_dob'] == true) {
echo ' born ' . $this->pref['birth_date'], ' ';
}
...
}
}
No. Inheritance is for extending classes that belong to the same type of class. For example:
Animal < Mammal < Primate < Human
How granular you make it (i.e. how many times you extend) depends on your needs.
The point, however, is that if a class has nothing to do with another class, or if they are only tangentially related, then they should not inherit from each other.
Something like settings should be passed to the class (i.e. the object).
So, paraphrasing the code in your question, you could do it like this:
// The settings should be created outside
$settings = new Settings;
// The settings are then provided to the new object
// Here we just pass it to the constructor, but you
// could also have something like a `useSettings()`
// method that sets it
$person = new Person($settings);
Extending code, like in your question, creates a tangled mess that you will be unable to untangle easily as your code matures. Writing self-contained units of code, and making use of interfaces, you can work on them separately and not worry about what the rest of your code does.
Situation
In this web app I am building there is a "bootstrap" sequence that defines (through constants) and initiates an extended controller. Currently, the controller keeps track of assets (script files, css, etc.) that will be deployed at the later render stage through a series of static variables. I will simplify the code here, think of it as pseudo-PHP.
/* CONTROLLER CLASS */
class Controller {
protected static $aryScriptFiles = array();
public function __construct() {
/* Behaviour */
/* Some logic that identifies/calls Home_Controller method Index */
}
public static function Add_Script($strFileName) {
static::$aryScriptFiles[] = $strFileName;
}
}
/* HOME_CONTROLLER CLASS */
class Home_Controller extends Controller {
protected static $aryScriptFiles = array('default', 'carousel', 'etc');
protected function Index() {
/* Behaviour */
/* Load the view as an include. It is "part" of the User_Controller */
}
}
/* EXAMPLE_HELPER */
class Example_Helper {
public static function Test() {
/* THE NEXT LINE IS IMPORTANT FOR THE QUESTION */
$objController = CONTROLLER;
$objController::Add_Script('dominoes');
}
}
/* INDEX VIEW FILE */
<h1>Welcome!</h1>
<?php
echo get_class(); <-- Would echo 'User_Controller'
Example_Helper::Test();
/* Simplification of render process */
foreach(static::$aryScriptFiles as $strFileName) {
/* Render the HTML script tag */
}
?>
Flow
Ok, given the above there is a bootstrap that ends up calling User_Controller. For examples sake, I have simply defined them to let you know what state the script will follow.
$strControllerName = 'User_Controller';
define('CONTROLLER', $strControllerName);
$objController = new $strControllerName();
What you end up with is the aryScriptFiles array having 4 entries and this works great.
Problem
Before reading on, please note I do not want to use magic methods, globals or have to pass a reference of the controller name to the Helper function.
I would like to try and remove the line in the helper file that pulls the current controller name to a variable from the constant.
$objController = CONTROLLER; <-- I want this to shoo shoo
If I were to just try and use the following, the script file that gets added by aid of the Helper is part of the original Controller array as opposed to the Home controller.
Controller::Add_Script('dominoes'); <-- Will not be part of the Home_Controller array
Question
Please can I have some opinions from the SO community on what you feel the best approach to tackle this would be taking in to account that the controller name will differ? My primary objectives in this exercise are:
Keep the View file VERY simple
Keep the Helper files simple.
Avoid the need to add any code more than necessary to the Home_Controller
I'm currently thinking that one of the best options would be to host the "assets" within a seperate class, just want to know whether it is possible.
Thanks for reading.
First of all, think about your seperation of concerns. Should it really be the responsibility of a controller to manage assets?. Why did you made the method for adding assets static in the first place?
I do not want to use magic methods, globals or have to pass a reference of the controller name to the Helper function.
What are you expecting? If you try to force a class to depend on another class in a completely different scope and context your only option is to use ugly hacks to make your object globally accessible.
Dependency Injection to the rescue
Why should your helper know about what controller and how the controller is treated from the outside?
The only thing your helper should do is to operate with the controller (in your case). It should not try to magically detect what controller is being used. It should just take a controller and operate with it.
class Example_Helper {
public static function Test($controller) {
$controller::Add_Script('dominoes');
}
}
Example_Helper::Test($objController);
Since the addScript() method and the $aryScriptFiles property is static anyways, you could also just call the method in the helper on the parent controller. It would make no difference.
Also why do you want to talk to your controller from the view? The view should be "dumb" it should not be able to hold and operate with data except those that were passed to it by the controller.
Wouldn't it make more sense to add functionality to your controller or one of it's services that passes the required assets to your view, instead of forcing the view to get it's data from from the controller by itself?
I think there are a few logical flaws in your code here. Especially your usage of static properties and methods. If you could clarify that a bit I could go in detail a bit.
Apart from architectural concerns (assets should indeed be managed by a separate AssetManager) your problem can be relatively easily solved because of PHP's rather peculiar own architecture, specifically exposed through methods like get_called_class. This allows you to write code like this:
$assets = []; // Global for brevity of example
class Base {
static function addScript($script)
{
global $assets;
$myName = get_called_class();
$assets[$myName][] = $script;
}
}
class Derived extends Base {
public function __construct()
{
self::addScript('test');
}
}
$foo = new Derived();
var_dump($assets);
Which will then output the following:
array(1) {
["Derived"]=>
array(1) {
[0]=>
string(4) "test"
}
}
Note that using get_class instead of get_called_class would here show the array's name as Base instead of Derived, while Derived is what you need. This way you can embed helper functions in Controller, which automatically derive the class name and forward it to the central asset manager.
I want to save some global vars to use in the website, like current user id, current user lever, and so on. Where is the best place to do it, or is it possible?
Setting it into constants.php is not working since "$this" is not recognized there.
The principal reason why I want this is because i don't like using sessions (I consider writing strings like $this->session->userdata('session_name') not so practical, writing something like CURR_UID is more easy to do it and read as well)
It's possible, but it isn't the way that Codeigniter was designed. Sessions are really the place for this kind of thing (namely, stuff that persists from one page view to the next), but you could wrap the session calls up in a library for beauty's sake if you wanted. Something like this:
// in libraries/User.php
class User {
protected $ci;
public function __construct() {
$this->ci = &get_instance();
}
public function id() {
return $this->ci->session->userdata('user_id');
}
// etc, etc.
}
Once you've written a few more helpers like id(), you can use them to access the relevant variables elsewhere in your application:
$this->load->library('user');
echo 'Current user ID is: ' . $this->user->id();
What you can do in this case is create a class My_Controller extends CI_Controller. Sort out all the functionality that you would need before actually loading any of the specific controller functionality.
Then any subsequent class you create you can do: class Whatever extends My_Controller.
Edit: I forgot to mention you should put the My_Controller class within the Application > Core folder.
I have a parent class, let's say class main { ... }, and an extension of it, let's call it class extension extends main { ... }.
My question is, how would I build another class, called class messages { ... }, which I can use inside the main class and the extended class of main, extension ? Besides the way I know, calling the class messages like this :
$messages = new messages;
$messages->someMethod();
Is there another way without having to do new ... to make the main and extension class inherit the methods inside the messages class ?
AFAIK, PHP does not support multiple inheritance, as others OOP languages do.
So, no, there is NOT another way.
And yes, you should create a property and instantiate the object inside the main class...
class main {
public $messages; // may be "protected" or "private" instead
public __construct()
{
$this->messages = new messages();
}
public do_something()
{
$this->messages->do_something_else();
}
}
However, there are alternatives to simulate a fake multiple inheritance.
An alternative would be: https://stackoverflow.com/a/356431/370290 - But I don't recommend this (even the own author doesn't).
Another alternative: https://stackoverflow.com/a/358562/370290 - IMHO, as weird as the previous one. :-)
And as of PHP 5.4.0 you can also use traits to achieve a "multiple inheritance" effect: http://php.net/manual/en/language.oop5.traits.php - This is very new at the moment.
You can't extend multiple classes and to exten the main class .. only a good thing if you extend from an abstract class.
But what you could do is add it in the construct of your main class like this:
//member variable for class main
public $_message = null;
public function __construct()
{
$this->_message = new Message();
}
Then whenever you need the message class just call $this->_message + the method you need (eg: $this->_message->addMessage())
don't forget to add this in you subclass:
public function __construct()
{
parent::__construct();
}
The problem you seem to have is that you can't do multiple inheritance (class YourClass extends main, messages).
The common feeling is that if you need multiple enheritance, you're doing something wrong in your design.
Every class is responsible for a single thing. A "extension" in this case "IS A" "main", but it is not a "messages", so it should not be a child of that. IF you need messaging capability, there is no 'shame' at all in just calling it like you suggest: you get yourself a nice object that knows how to message, and play with that. There is no real need to do it differently.
If you're looking for alternatives (which you really don't need as far as I can see!) you could make it a class with a bunch of static methods, and just call it like messages::someMethod(), but I think that would be considered an anti-pattern in this case.
Just go with it: messages are created by an object of type message. So you make one, and call the function. In the end, if you ever need big changes (database connection, logging, etc etc) for you messaging, you can do this all in your nice and cosy messaging class. Everyone happy :)
You should create a class inside a class. Just like in this question.
Then, you can use $this->someclass->function.
Note: construct needs to be $this->someclass = new Whatever() too.
I have a controller with different methods, but they all have to set a variable containing a list of items to be shown in a box in the view, I extract data from the DB and set $data['categories'].
Can I set it once and have it visible by all methods?
In addition to this, if you are only using $this->data to get the values into your views, instead of doing:
$this->data->something = 'whatever';
Then doing
$this->load->view('something', $this->data);
You can instead set it with:
$this->load->vars('something', 'whatever');
Then later on use the normal localized $data array (or whatever you like) as the variable will be globally available to all loaded view files.
I'm not suggesting either way is better, just letting you know how else it could be done. I personally use a mix of these methods. :-)
make it a property of the class
class Controller {
protected $data;
and use '$this' to access in in your methods:
class Controller {
function foo() {
$this->data etc...