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.
Related
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 have a variable that has the first URL segment. I want to use this as a class on the body tag. It will mainly be used for setting links in my navigation as being active. Is there a way that I can create this variable in one place and use it in all of my controllers? Its not that big of a problem to set it in all of my controllers but I'd like to keep my code as clean as possible. This is what I have in my controllers right now:
$url_segment = $this->uri->rsegment_array(); //get array of url segment strings
$data['url_segment'] = $url_segment[1]; //gets string of first url segment
Is there a way to only have the code above ONCE in my app, instead of inside all of my controllers? If so where should I place it?
I'd extend CI_Controller with a custom subclass that includes that variable, then have all the actual controllers extend that. CodeIgniter makes it easy - just create application/core/MY_Controller.php containing something along these lines:
class MY_Controller extends CI_Controller {
private $cached_url_seg;
function MY_Controller() {
parent::construct();
$url_segment = $this->uri->rsegment_array(); //get array of url segment strings
$this->cached_url_seg = $url_segment[1]; //gets string of first url segment
}
}
And then change your controllers to extend MY_Controller.
You'll still have to add it to $data in each individual controller, but I suppose if you wanted you could add private $data to MY_Controller, too.
You might want to consider making a 1 time library file, with all features you want to be globally accessable, then in your autoload.php add this library there so it initializes automatically..
class my_global_lib {
protected $CI;
public $segment;
public function __construct(){
parent::__construct();
$this->CI =& get_instance();
// Do your code here like:
$this->segment = $this->CI->uri->segment(1);
// Any other things you want to have accessable by default could go here
}
}
This would allow you to call this from your controller like so
echo $this->my_global_lib->segment;
does this help?
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();
Basically in codeigniter, how can i use the system and application libraries within other libraries? So say I have a application library such as:
class User
{
// some instance variables
}
Now lets say I want to use codeigniter session class in the User class, basically being able to work with sessions inside the User class. How can I go about doing this?
The typical way to do this is to use get_instance(), which returns an instance of Codeigniter (actually the current controller class).
This will work from anywhere in your CI application:
get_instance()->session->set_userdata('key', 'value');
$segments = get_instance()->uri->segment_array();
// etc.
Typically in Codeigniter, you assign it to a class variable for ease of use:
class User
{
private $CI;
function __construct()
{
$this->CI = get_instance();
}
// Example
function login()
{
$user = $this->get_user();
$this->CI->session_set_userdata('user_id', $user->id);
}
}
This does create a lot of dependency on the state of CI and the different loaded classes, rather than passing instances of them in directly to the User class via Dependency Injection, but this is the typical flow for someone developing a CI library.
Ideally you would just pass in the session data you needed from your model or controller the loads the library.
Otherwise, this should work from any file:
<?php
ob_start();
include('index.php');
ob_end_clean();
$CI =& get_instance();
$CI->load->library('session'); //if it's not autoloaded in your CI setup
echo $CI->session->userdata('name');
?>
I found this answer on a similar question.
I have a view helper that manages generating thumbnails for images. The images are stored using a unique ID and then linked to a file resource in the database.
I am trying to find out if it is possible for the view helper that generates these images to access the model or controller directly, as it is not possible to load the image data at any other point in the controller work flow.
I know this is a bit of a hack really, but it is easier than trying to rebuild the entire data management stack above the view.
If you had set the data in the model or controller you could access it. So you'd have to think ahead in the controller. As you said you can't load it in the controller, perhaps you need to write a specific controller function, which you can call from the view using $this->requestAction() and pass in the image name or similar as a parameter.
The only disadvantage of this is using requestAction() is frowned upon, as it initiates an entirely new dispatch cycle, which can slow down your app a bit.
The other option, which may work is creating a dynamic element and passing in a parameter into the element and have it create the image for you. Although I'm not too sure how this would work in practise.
How are you generating the thumbnails using the helper in the view if you aren't passing data into it from a controller or model? I mean if it was me, I would be setting the 'database resource' in the controller, and passing it to the view that way, then having the helper deal with it in the view. That way you could bypass this issue entirely :)
$this->params['controller'] will return what you want.
According to the ... you can put this code in a view.ctp file then open the URL to render the debug info:
$cn = get_class($this);
$cm = get_class_methods($cn);
print_r($cm);
die();
You could write a helper and build in a static function setController() and pass the reference in through as a parameter and then store it in a static variable in your helper class:
class FancyHelper extends FormHelper {
static $controller;
public static function setController($controller) {
self::$controller = $controller;
}
... more stuff
}
Then in your Controller class you could import the FancyHelper class and make the static assignment in the beforeFilter function:
App::uses('FancyHelper', 'View/Helper');
class FancyController extends AppController {
public $helpers = array('Fancy');
function beforeFilter() {
FancyHelper::setController($this);
}
... more stuff
}
And then you could access the controller from other public functions inside FancyHelper using self::$controller.
You can check the code(line ☛366 and
line ☛379) of the FormHelper, try with:
echo $this->request->params['controller'];
echo Inflector::underscore($this->viewPath);