Yii2 - How to call ActiveRecord method given its name - php

I have a string variable that contains the model's class name, and I want to call a method on said model using that variable, is that possible??
My code:
foreach($tables as $tables)
{
$table = ArrayHelper::getValue($tables, 'table_name');
$model = \common\models\$table::findAll();
var_dump($model);
}
A simpler version:
$table = "DataAnalysis";
$model = \common\models\$table::findAll();
var_dump($model);
When I run that code, I get the following error:
Exception 'ParseError' with message 'syntax error, unexpected '$table' (T_VARIABLE), expecting identifier (T_STRING)'
Is there anything I can do to call model given the string contained in the variable?

You should simply do,
$model="\\common\\models\\DataAnalysis";
$model::findAll();
OR
$table="DataAnalysis";
$model="\\common\\models\\{$table}";
$model::findAll();
rather than call_user_func() thats too much code for a simple task
Edit
If you need to instantiate the class instead of static call, you can simply do
$table="DataAnalysis";
$model="\\common\\models\\{$table}";
new $model();

You can do it using call_user_func().
// If you have the name of the ActiveRecord class, like in you comment
$class = 'DataAnalysis';
/** #var ActiveQuery $query */
$query = call_user_func($class . '::find');
// Then use the query as you want
foreach($query->each() as $model) {
// Do something with your model
}
If you are not sure if the value in the variable will always be correct, wrap it on a try/catch.
If what you have is the table name, and not the class name, you can try to convert with the Yii inflector camelize method.
use yii\helpers\Inflector;
$tableName = 'some_snake_case_name';
$className = Inflector::camelize($tableName);
/** #var ActiveQuery $query */
$query = call_user_func($className . '::find');
// If you want to filter results
$query->where(...);
// Then use the query as you want
foreach($query->each() as $model) {
// Do something with your model
}
call_user_function docs are here.

Related

how i can make find method does not make a problem

i create a Depot class. when i create object from this class i use find method for find a Special item with id.
after that i cant call any other method.
I do not use Laravel
// index.php file
$depot = new Depot();
$depot = $depot->find(2);
var_dump($depot->hi());
Fatal error: Uncaught Error: Call to undefined method stdClass::hi()
hi method is for test.
// model.php file
class Model {
// ...
public function find(int $id)
{
$statement = $this->pdo->prepare("select * from {$this->table} where id = :id");
$statement->execute(compact('id'));
$obj = $statement->fetch(PDO::FETCH_OBJ);
return $obj;
}
}
class Depot extends Model {
//...
public function hi()
{
echo "hi";
}
}
With this line:
$depot = $depot->find(2);
you're overwriting the variable $depot, representing your object, with the result of your query. The object returned (unsurprisingly) doesn't contain a function called hi().
I don't know if this was just a typo, but if not, it's generally a sign of poor code quality if you re-use the same variable to contain two completely different things. It leads to maintenance and readability issues, and often causes errors further down the line, such as this one, where you mistakenly assume the variable still has its original content. Weakly-typed languages such as PHP are especially vulnerable to this kind of mistake. The easiest thing is to just make a rule never to do it.
Assigning the result to a different variable, e.g.
$depot = new Depot();
$findResult = $depot->find(2);
$depot->hi();
will fix the issue.
(Also the var_dump() was unnecessary since hi() already contains an echo.)
try this
$depot = new Depot();
$depotDb = $depot->find(2);
var_dump($depot->hi());

how to reset default scope in yii2

I am using default scope in my project and it works fine.
public static function find()
{
return parent::find()->where(['is_deleted' => 0]);
}
But now, I want to show all the deleted records in the report section.
How can I skip default scope for particular query only?
Use this to clear or redefine your condition:
$model = Model::find()->where('');
If you want to make sure that you're using fresh query (without any params or conditions), you need to create new ActiveQuery object for given model.
$query = Yii::createObject(ActiveQuery::className(), [Post::class]);
Or add a helper method in model itself:
public static function freshFind()
{
return parent::find();
}
and use it instead of Post::find().
You could avoid the use of find() ..using a findBySql
$sql = 'SELECT * FROM product';
$product= Product::findBySql($sql,)->all();
in this way you all the models of product ..
and you could also use
$sql = 'SELECT * FROM ' . Product::tableName() ;
for avoid explici table name for Products

Could not call class constructor

I've been busy trying to create my own framework (to become more experienced in this area), and stumbled on an error I couldn't fix by searching google... wow...
I want to get data from a database, placed in an object / class. I've done it before, in a different way I learned at school, but I wanted to tweak it and make it more dynamic so I could use it in my framework.
The problem I stumbled on is the following:
SQLSTATE[HY000]: General error: could not call class constructor on line 96
This is the function in my database class:
public function getObject($query, $classRootPath)
{
try {
//Check if slashes are double already and make them if not
if(!strpos($classRootPath, "\\\\")) {
$classRootPath = str_replace("\\","\\\\",$classRootPath);
}
$statement = $this->pdo->prepare($query);
$statement->execute(\PDO::FETCH_CLASS, "Campers\\Camper"); // I want this path to be $classRootPath once it is working with this dummy data
return $statement->fetchAll();
// return $this->pdo->query($query)->fetchAll(\PDO::FETCH_CLASS, "Campers\\Camper");
} catch (\PDOException $e) {
throw new \Exception("DB receive object failed: " . $e->getMessage());
}
}
This function is nested in Database and the class is called Database / Database.php
The following class is nested in Campers and is called Camper.php
class Camper {
public $ID, $date, $camperID;
public function __construct($ID, $date, $camperID)
{
$this->ID = $ID;
$this->date = $date;
$this->camperID = $camperID;
}
}
The only reason I can think of this is not working, is that the call "Campers\\Camper" is calling on top of Database, but I don't know how to escape that. I tried with ..\ but I got errors back, and this is the closest I can get. Here it can find the class though, but it can't find the constructor of Camper...
I've tested if my db class / connection works, so that's not the fault.
The structure of my table matches my Campers class constructor.
From the PSR-4 spec:
The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
You likely can't instantiate that Camper class as-is anyway. PSR-4 expects your filename to match the class. It should be located in framework/Campers/Camper.php.
This error implies more than been unable to call the constructor, it is also used to indicate than an error occurred while calling it.
In my case, an Exception was been thrown inside de constructor. If you don't print/log the stacktrace, you could easily miss it.
Enjoy!
:)
I had the same issue in at least 3 cases.
Case 1: You select something from the database that can contain a NULL value.
SELECT name FROM tableX;
In that case I do the select in that way:
SELECT IFNULL(name,'') AS name FROM tableX;
where name is a field in your class.
Case 2: You select something that is not a field in your class
class Example {
public string $name = '';
}
Then the following query will fail as id is not declared in your class
SELECT id, name FROM tableX;
case3:
your field in the class isn't initialised
class Example {
public string $name;
}
SELECT name FROM tableX;
can be solved by either initialise the field
class Example {
public string $name = '';
}
or using a constructor to declare it
BR

Laravel 4 - Using Eloquent Models in a custom library class

I made a library class that I am using for some common functions not provided by Laravel. It's been loaded into /config/app.php under the 'aliases' array, so that shouldn't be the problem.
When I call a method from my class ("InfoParse"), my conroller returns a blank page. I think this has to do with the fact that I'm calling a method from the library which uses Eloquent to interface with the database. I tried adding
use Illuminate\Database\Eloquent\Model;
to the top of the file, but that didn't help either.
Is there a specific way I should be setting up my class file so I can use either the DB:: class or Eloquent class?
Below is the function in question:
/**
* Check to see if this student is already recorded in our student table.
* If not, add the entry, then return true.
* #param int $cwid
* #return boolean
*/
public static function checkStudentTableRecords($cwid)
{
if(Student::where('cwid', '=', $cwid)->count() != 0)
{
return TRUE;
}
else
{ ##insert the student into our student table
$studentInfo = self::queryInfoFromCWID($cwid);
$studentEntry = new Student;
$studentEntry->cwid = $cwid;
$studentEntry->fName = $studentInfo['fName'];
$studentEntry->lName = $studentInfo['lName'];
$studentEntry->email = $studentInfo['email'];
$studentEntry->save();
return TRUE;
}
}
(note: the self::queryInfoFromCWID() function is calling a function defined earlier in the class)
After some investigation, it turns out I need to format my Eloquent Model calls like this:
if(\Student::where('cwid', '=', $cwid)->count() != 0)
...
$studentEntry = new \Student;
The backslash is necessary to avoid namespace collision within the Laravel4 application.

Cast codeigniter database result object to custom object

Codeigniter can return a database query as generic "Object" like:
$q = $this->db->get("some_table");
$obj = $this->q->row();
$var = $obj->some_property
In my case I want to make a PHP class who's public variables are 1 for 1 with the database columns, along with some public methods. Is there a quick one-shot way to cast or convert the generic "Row" object into my custom class object? I've read posts that hint that it is certainly possible, but most involve a really hacky serialize/deserialize solution. In the past I have just done:
public function __construct($row) {
$this->prop = $row->prop;
$this->id = $row->id;
$this->value = $row->value;
}
And I find this is very tedious and makes ugly code.
See the third section under result():
CodeIgniter User Guide: Generating Query Results
You can also pass a string to result() which represents a class to instantiate for each result object (note: this class must be loaded)
$query = $this->db->query("SELECT * FROM users;");
foreach ($query->result('User') as $row)
{
echo $row->name; // call attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
}

Categories