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
Related
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.
⛳ What I need:
I am developing an application in Laravel 5.4 and I want a global scope that allows me to filter different elements of the application depending on the user that created them.
🌍 My global scope:
I have a class BaseEloquentModel.php who extends Eloquent and all my models extends from this class. I have a global scope as follow:
protected static function boot()
{
parent::boot();
static::addGlobalScope('', function(\Illuminate\Database\Eloquent\Builder $builder) use($userId) {
/**
* I get the name of the table with <code>(with(new static))->getTable()</code>
* and then filter the query for the <b>user_id</b> field
*/
$builder->where(
(with(new static))->getTable() . '.user_id',
'=',
$userId
);
});
}
⛔ The problem
When I have a query like this, with or operator, the global scope is "neutralized":
$qBuilderCars = Car::whereRaw("name like ? or id = ?", [
'%' . $searchCriteria. '%',
$searchCriteria
]);
If I call the toSql() method on $qBuilderCars I see that it "correctly" adds the AND operator to the end of the query.
select * from `cars` where name like ? or id = ? and user_id = ?
Maybe you've already noticed my problem ... If the element's builder, in this case cars, has used an OR operator, then the global scope will not help, since there is no parenthesis between where name like ? or id = ?. So the resulting query would be something similar to the following:
select * from `cars` where name like ? (or id = ? and user_id = ?)
So this query will return all cars whose name matches or whose ID is the one received and has been created by the user...
When what I need is:
select * from `cars` where (name like ? or id = ?) and user_id = ?
👎 My attempts
I tried to alter my global scope to try to make the AND operator that I add the most restrictive in the query, but without any success.
I can not manually add parentheses to all the application's queries, so ... Is there a way to add global parentheses from the global scope to the builder?
💡 The solution
The solution is to add parentheses to all the raw queries.
✅✅ You can see the #Styx solution which I consider the most successful
I will also leave my answer, which acts directly inside the global scope, and which I consider interesting to be able to see how an \Illuminate\Database\Eloquent\Builder object works
Well, it seems that your solution to add parentheses is the best workaround, but I have a suggestion how to do that slightly better way.
Create new class QueryBuilder. For example, in \App\Models\ namespace (app/Models/ folder):
namespace App\Models;
use Illuminate\Database\Query\Builder as EloquentQueryBuilder;
class QueryBuilder extends EloquentQueryBuilder {
public function whereRaw($sql, $bindings = [], $boolean = 'and')
{
return parent::whereRaw('('.$sql.')', $bindings, $boolean);
}
}
Add this code to your BaseEloquentModel class:
use Illuminate\Database\Eloquent\Model;
use App\Models\QueryBuilder; // <-- addition
class BaseEloquentModel extends Model {
// ...
protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();
return new QueryBuilder(
$connection,
$connection->getQueryGrammar(),
$connection->getPostProcessor()
);
}
// ...
}
Now, all whereRaw() calls will automatically have parentheses around query.
😕 The solution...?
I found a "solution", before apply my global scope I loop through all where clauses whose type is raw:
protected static function boot()
{
parent::boot();
static::addGlobalScope('', function(\Illuminate\Database\Eloquent\Builder $builder) use($userId) {
/**
* My workaround to prevent a raw query from neutralizing the global scope.
* We go through all the queries and, if they are raw, encapsulate them in parentheses.
*/
$wheres = $builder->getQuery()->wheres;
foreach ($wheres as $iWhere => $where) {
// If where clause is "raw" I wrap with parenthesis.
if ($where['type'] == 'raw') {
$builder->getQuery()->wheres[$iWhere]["sql"] = "({$where['sql']})";
}
}
/**
* I get the name of the table with <code>(with(new static))->getTable()</code>
* and then filter the query for the <b>user_id</b> field
*/
$builder->where(
(with(new static))->getTable() . '.user_id',
'=',
$userId
);
});
}
I do not know if this solution will have any unexpected repercussion or if it will affect in excess the performance of the queries...
Will return the next SQL:
select * from `cars` where (name like ? or id = ?) and `cars`.`user_id` = ?"
First I have to say that I tried to find solution, and i didn't.
Basic question:
$Br = new BrandTop;
dd( $Br->limit(10)->get() ); // Will return 10 rows
and
$Br = new BrandTop;
$Br->limit(10);
dd( $Br->get() ); // Will return all rows.
So, the basic question - why? How can I set some limit for Model, but still work with it, for example set (or not set) some where or order depends on other variables.
Advanced question:
I want to use Model like this:
class BrandTop extends Model
{
public function withBrand() {
return $this->leftJoin('brand', 'brand.id' , '=', 'brandtop.brand_id');
}
public function forType($type) // there is much more conditions for type
{
return $this->where(['type' => $type]);
}
// main function
public function forSunglasses($limit = 0, $logo = false)
{
if ($logo)
$this->where(['menu_logo' => 1])->orderBy('total_sales', 'desc');
if ($limit)
$this->limit($limit);
return $this->forType('sunglasses')->withBrand();
// But there goes Error, because forType() return Builder object, and it has no withBrand() method
}
}
So, there is much more conditions, and it's much easier to set all conditions in separate methods. But how?
Model vs Builder
The thing to understand here is the difference between the Model object and the underlying Builder (query builder) object.
The statement $Br = new BrandTop; will create a new instance of a Model, and assign it to the $Br variable. Next, the $Br->limit(10) statement will create a new instance of a Builder object for the brand_tops table, with a limit of 10 applied.
In your first example, by doing $Br->limit(10)->get(), you're calling get() on the Builder that has your limit applied.
In your second example, your individual $Br->limit(10) creates the new Builder instance, but never uses it for anything. The next statement, $Br->get(), creates another new Builder instance without any constraints, so it retrieves all the records.
To be able to build up your query, you need to assign your Builder instance to a variable, and continue to modify that instance before finally calling get(). For example, to get your second example to work:
$query = BrandTop::query();
$query->limit(10);
$query->where(/*conditions*/);
dd($query->get());
Query Scopes
In relation to the second part of your question, you probably want to look into query scopes.
class BrandTop extends Model
{
// renamed to "JoinBrand" instead of "WithBrand", as "with" would imply
// an eager loaded relationship vs a joined table
public function scopeJoinBrand($query)
{
return $query->leftJoin('brand', 'brand.id' , '=', 'brandtop.brand_id');
}
// got rid of "for" prefix
public function scopeType($query, $type)
{
return $query->where('type', $type);
}
// got rid of "for" prefix
public function scopeSunglasses($query, $limit = 0, $logo = false)
{
if ($logo)
$query->where(['menu_logo' => 1])->orderBy('total_sales', 'desc');
if ($limit)
$query->limit($limit);
return $query->type('sunglasses')->joinBrand();
}
}
With the above model, your code would look something like:
dd(BrandTop::sunglasses()->get());
// or, more verbosely:
$query = BrandTop::query();
$query->sunglasses(); // $query already an object, no need to reassign it to itself
dd($query->get());
I am passing a parameter from model to view like this
Model
class school_model extends CI_Model{
function employee_get($student_id){
$query = $this->db->get_where('students', array('student_id'=>$student_id));
return $query->row_array();
}
}
Controller
function bret($student_id){
$this->load->model('school_model');
$data = $this->school_model->employee_get($student_id);
echo $data['student_gender'];
}
This obviously translates to select * from students where id=id given for example and seen through the browser like http://example.com/env/at/index.php/frontpage/bret/306
I am wondering if the get_where() is suitable if i wanted to have this query
select student_gender,student_has_a_medical_condition from students where (student_gender = 'female' && student_has_a_medical_condition = 'no') LIMIT 40;
Will i need to extend get_where() for it to work?.
First,I suggest reading the excellent documentation for CodeIgniter on the ActiveRecord class.
You don't have to extend get_where() but just use the existing methods to define your query:
function employee_get($student_id){
$this->db->select('student_gender','student_has_a_medical_condition');
$query = $this->db->get_where('students', array('student_id'=>$student_id,'student_has_a_medical_condition'=>'no','student_gender'=>'female'),40);
return $query->row_array();
}
Of course you can pass in the additional parameters to the function so they are not hardcoded, and you can also pass in the desired columns as a paremeter, too. But start with the documentation.
i'm new to zend framework, in this simple function i want to get a single 'post' and then i want to find all the comments in the related table
public function getPost($idPost)
{
$db= Zend_Registry::get('db');
$select=$db->select()
->from($this->_name, '*')
->where("idPost= ".$db->quote($idPost, 'INTEGER'));
$stmt=$select->query();
$rowset=$stmt->fetchAll();
$post=$rowset->current();
//ora devo aggiungerci i commenti che questo post ha ricevuto
$comm=$post->findDependentRowset('commenti');
$ris=array($post, $comm);
return $ris;
}
in my index controller i i simply call this function, but i get this error:
Call to a member function current() on a non-object in C:\xampp\htdocs\...
where's the mistake?
I think you have a few misconceptions about how you're using Zend_Db.
1. You're not using the ORM, just the PDO wrapper
Which means, your queries won't return Zend rowsets and rows and therefore you can't use the methods of you can use on those.
2. The default fetch mode
The default fetch mode of the Zend_Db_Statement fetchAll() method is array, if you want it to return an object (stdClass), change the fetch mode before fetching the data:
$stmt->setFetchMode(Zend_Db::FETCH_OBJ);
3. Using fetchAll() when you actually want one row
If you just want one row, then don't fetch a whole table! With Zend_Db_Statement, use for example:
$row = $stmt->fetch();
or
$rowObj = $stmt->fetchObject();
... again, that's not a zend row object, just a stdClass instance, but you can do:
$rowObj->some_field;
on it.
On the other hand, if this is a method in your Post model, it should look something like:
public function getPost($idPost)
{
return $this->getRow($idPost);
}
This will return the post, then, if you've setup the table relationships correctly, you can also query for the dependent data or just get all comments with that id separately.
The problem is that unless you define a table class as was previously mentioned you can't uuse the dependent or parent rowsets.
To make your current function work would be best done with two functions, and keep it simple:
public function getPost($idPost)
{
$db= new Zend_Db_Table($this->_name);
$select=$db->select()
->where("idPost= ?", $idPost);
/*Fetch just the row you want, or use fetchAll() if you need to match return types*/
$row = $db->fetchRow($select);
return $row;
}
public function getComments($table='comments', $id) {
$db = new Zend_Db_table($table);
$select = $db->select()->where('post_id = ?', $id)->order('date ASC');
$rowset = $db->fetchAll($select);
return $rowset/* or you could return an array ->$rowset->toArray() */
}
Zend_Db_Table is going to attempt to use the current database adapter, so all you need to do is pass in the tablename.
One more note: you don't need to use any of the quote() function when using select() it's taken care of.
But it is really important, that if you are going to use Zend_Db, you need to learn about "Defining table classes". At least enough to use them in your own classes.
I hope this helps!
To get a rowset and dependent rowset you have to use Zend_Db_Table.
You only use the Zend_Db_Adapter with Zend_Db_Select.
Read from here.
So you have to define a class which extends from Zend_Db_Table_Abstract.
Example:
class Bugs extends Zend_Db_Table_Abstract
{
protected $_name = 'bugs';
protected $_primary = 'bug_id';
}
To get the Zend_Db_Table_Rowset object use:
$bugs = new Bugs();
$rowset = $bugs->fetchAll("bug_status = 'NEW'");
To find dependent rowsets you have to define the relation in your table class. Look here how to define relationships.