The question I originally had has been asked (and answered) several times before1,2,3,4 : how do I call a variable defined in a different function in the same class - but couldn't get the following to work:
class my_model extends CI_Model {
public $myvar;
public function test1()
{
$this->myvar = "Hello world";
}
public function test2()
{
return strtoupper($this->myvar);
//And have tried:
// return my_model::test1()->$myvar
}
}
Changing test1 to act as a setter function (and calling that function to access the variables) does work; changing my test1 to:
public test1()
{
$this->myvar = "Hello world";
return $this->myvar;
}
and test2 to:
public function test2()
{
$a = $this->test1();
return strtoupper($a);
}
This question has a comment that controller methods in CodeIgniter work slightly differently from regular PHP class methods. Is this also the case for model classes: if so is this the reason I need to call test1 first (and that variables in other methods aren't accessible), or is there something else I've failed to understand?!
Real life example
This is what I'm actually trying to do - please tell me if there's a better way to do it!
I was going to split up the methods which run a select * query and a query which returns a count, and since I'm using CI's QueryBuilder class this seemed the obvious solution:
class my_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
public function test1()
{
return $this->db->get('Table1');
}
public function test2()
{
return $this->test1()->result_array();
}
public function test3()
{
return $this->test1()->num_rows();
}
}
However, cale b made a comment that this might feel a bit jenky - how else would it be done?
cale_b provides you with the correct answer. My intent here is to say the same thing in a different way.
Your Class
class my_model extends CI_Model
{
public $myvar;
public function test1()
{
$this->myvar = "Hello world";
}
public function test2()
{
return strtoupper($this->myvar);
}
}
Here you MUST call test1() before you call test2() because $myvar has not been given a value (set equal to something) until test1() is executed.
There are multiple ways to accomplish the task of giving $myvar a value.
Set a default value when it is declared
class my_model extends CI_Model
{
public $myvar = 'Hello World';
public function test1()
{
...
In the next example $myvar is set in the class constructor which works more or less exactly the same as the above. I also declare 'setter' and 'getter' functions. Notice I made $myvar private for reasons that will be illustrated in the usage example.
class Test_m extends CI_Model
{
private $myvar;
public function __construct()
{
parent::__construct();
$this->myvar = "Hello world";
}
//a 'setter'
public function set_myvar($newValue)
{
$this->myvar = $newValue;
}
//a 'getter'
public function get_myvar()
{
return strtoupper($this->myvar);
}
}
Now for a usage example. Here's a controller
class Teststuff extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('test_m');
}
function index()
{
echo $this->test_m->get_myvar()."<br>"; //outputs HELLO WORLD
//change the value of $myvar
$this->test_m->set_myvar("Hello Stackoverflow");
echo $this->test_m->get_myvar()."<br>"; //outputs HELLO STACKOVERFLOW
//try to access $myvar directly
echo $this->test_m->myvar;
//produces a PHP error "Undefined property: Teststuff::$myvar"
}
}
The error is because $myvar is private. Change the declaration in Test_m to public $myvar; and the output will be 'Hello Stackoverflow'.
Again, this restates what cale_b stated and he deserves the accepted answer.
There may be some misunderstanding of terminology.
In your first code example, test1 is a setter. All a setter is supposed to do is set a class variable - potentially accepting the value, if desired. Setters do not necessarily return anything.
Example:
// This is an example of a setter
$this->setMyVar( $var ) {
$this->myVar = $var;
}
In your second code example, you've actually caused test to be both a setter and a getter - first it sets the value, then returns the value:
// This is an example of a setter AND a getter in one
$this->setMyVar( $var ) {
$this->myVar = $var;
return $this->myVar;
}
Now, back to your first example, and your question:
As you stated - the reason it does not work is because the getter method (test2) attempts to get a value from the class variable, but the class variable has not yet been set.
So, the class variable must be set first, somehow, somewhere.
So, to modify your first sample code so that it WOULD work, you would make one of four possible changes:
Option 1:
Declare your variable with the desired value in the top of the class:
public $myvar = 'Hello World';
Option 2:
Before calling your getter (elsewhere in your code), call your setter first:
// Where used inside of some other function where you need my_model->myval
my_model->test1();
$value = my_model->test2();
Option 3:
Set the value in a constructor. Adding this code to the top of the class will cause the variable to be immediately populated when the class is constructed:
public function __construct() {
$this->myvar = 'Hello World';
}
Option 4: Or, call your setter from within your getter. This feels jenky, but may be the right solution in some circumstances:
public function test2() {
// First call test1 to set the variable
$this->test1();
// Now $this->myvar has a value, so return it
return $this->myvar;
}
As a side-note, as someone who has had to maintain code that was written years ago, I can attest to the value of naming functions in descriptive / meaningful ways. Calling them some variation of set and get, along with a meaningful variable name, helps troubleshooting later.
Made-up example:
class my_model extends CI_Model {
// Flag for debug. When true, output debug messages
public $debug;
public function setDebug( $debug = TRUE ) {
$this->debug = $debug;
}
public function getDebug() {
return $this->debug;
}
// ... other class methods here ...
}
EDIT
Based on revisions to the question, I'm offering these (potential) adjustments to the added code in the question:
class my_model extends CI_Model {
private $results = FALSE;
public function __construct()
{
$this->load->database();
}
private function load_results()
{
// Could use or remove the logic here, if data remains constant across a single page load keep it, if not, remove it
// Only hit the database if the $results haven't yet been loaded
if ( $this->results === FALSE ) {
$this->results = $this->db->get('Table1');
}
}
public function test2()
{
$this->load_results();
return $this->results->result_array();
}
public function test3()
{
$this->load_results();
return $this->results->num_rows();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With