Eloquent: Do I need to do this query raw? - laravel

I'm rewriting parts of legacy software using laravel/eloquent.
my understanding of typical eloquent is good and I've done some projects in laravel but not so much for legacy code.
I'm trying to do this in eloquent which creates 79 rows:
INSERT IGNORE INTO `ps_module_shop` (`id_module`, `enable_device`, id_shop) (SELECT `id_module`, `enable_device`, 555 FROM ps_module_shop WHERE `id_shop` = 1);
I've read you can only do bulk insert with model::insert but I'm not sure how to do so with this query specifically, other than pure sql.
This is how I'm doing it for now, but raw sql feels more elegant for this query:
$blkInsert = [];
$tplModuleShop = ModuleShop::where('id_shop', 1)->get();
foreach($tplModuleShop as $mshop) {
$blkInsert[] = [
'id_module' => $mshop->id_module,
'enable_device' => $mshop->enable_device,
'id_shop' => $shop->id_shop,
];
}
ModuleShop::insert($blkInsert);
Note: I know this is a pivot table, but it is legacy db that uses composite keys so I decided to treat it as its own model.

Honestly this might just be easier using your sql statement... here is my attempt:
DB::table('ps_model_shop')
->insert(DB::table('ps_module_shop')
->select(['id_module', 'enable_device'])
->where('id_shop', 1)
->get()
->map(function($module){
$module['id_shop'] = 555;
})
->toArray()
);

We found a way to do it with DB::select (although still doing the sql part "raw")
$fixCollection = function($collection) {
return json_decode(json_encode($collection), true);
};
ModuleShop::insert(
$fixCollection(
\DB::select("SELECT `id_module`, `enable_device`, {$shop->id_shop} as id_shop FROM ps_module_shop WHERE `id_shop` = 1")
)
);

Related

Duplicate Queries in Laravel- Debugger

I am trying to update the columns of the records based on the other column records but it results in the duplicate queries since its being executed for each record in the table.I s there a way to avoid it and improve this code.
public function updateReviewColumn(){
$reviewData = $this->pluck('indicator')->toArray();
foreach ($reviewData as $datum) {
if ($datum == 'Y')
$this->where('indicator', $datum)
->update(['reviewindicator' => 'Yes']);
else
$this->where('indicator', $datum)
->update(['reviewindicator' => 'No']);
}
}
I would transform this following query into a eloquent stmt:
UPDATE yourTable
SET indicator = IF(indicator = 'Y', 'Yes','No'),
WHERE indicator= 'Y' OR indicator= 'N'
To have it one shot I think you should go for DB raw query (if you got MySQL behind)
Otherwise you can go for two different Eloquent stmt
MyTable::where('indicator', 'Y')->update(['indicator' => 'Yes']);
MyTable::where('indicator', 'N')->update(['indicator' => 'No']);
Probably you'll face issues if you got safe_updates enabled in MySQL

Updating multiple tables in codeigniter

I have a function in codeigniter that i want to use to update two tables. This is the code
if($loss_making_trade_amount > 5 && $loss_making_trade_amount < 20){
$user_data = array(
'trading_balance' => $trading_balance_float - 0.50
);
$data = array(
'trade_consequence' => '0.50',
'loss_in_amounts_cron_status' => 'seen'
);
$where = "id='$rid'";
$where_trading_balance = "email='$email'";
$this->db->where($where);
$this->db->set($data);
$this->db->update('mailbox_ke_01', $data);
//update users table at this level
$this->db->where($where_trading_balance);
$this->db->set($user_data);
$this->db->update('users', $user_data);
}
Will i be able to update the tables in the way i have done or will $this be pointing to the first table when updating the second table?.
Your code looks fine, once a query ran - you don't have to be worry about because the Query Builder resets itself after every query if it is finished - or dies in case of a DB Error.
You can take a closer look here
The documentation also clearly suggests that the query runs if the update function gets called.
$this->db->update() generates an update string and runs the query based on the data you supply.
For more information read the documentation here
The only thing i would change are your where clauses:
instead of
$where = "id='$rid'";
$this->db->where($where);
you should use the where function of the Querybuilder properly
$this->db->where("id", $rid);
The same applies for your $where_trading_balance

Laravel query builder vs Raw

Im quite new to laravel and so far I realy enjoy eloquent and querybuilder, but as soon as the queries become more complex, my head starts to hurt... I have just finished 2 working examples after quite some time, maybe you guys can help me optimize it. I will first give the examples, then the (dis)advantages
So first the DB::select... which honestly, I think is more readable then the 2nd example.. or maybe I am just doing the builder thing wrong xD.
$shared_slugs = Magic::in_query($this->shared_slugs);
$items = DB::select("
SELECT * FROM f_admin_items
WHERE
active = 1
AND (
slug IN $shared_slugs
OR
:treshhold <= :urank AND id IN (SELECT admin_item_id FROM f_user_access WHERE user_id = :uid)
OR
:treshhold > :urank AND `rank` >= :urank
)
ORDER BY `order` ASC
", [
'treshhold' => $this->threshold_rank,
'urank' => $user_rank,
'uid' => $user_id
]);
Overall pretty effective with the named parameter binding. Basicly the menu items always have to be active, plus (1 OR 2 OR 3 ). For example dashboard is accepted as shared slug.. Otherwise there are some ranking checks.
Now I have been trying hard to do this with eloquent en query builder =/ I didn't think setting up a relation was not necessary because I only use this for the menu and a middleware. In this case the f_user_access table containing only admin_item_id and user_id, doesn't really function as a pivot and isn't used anywhere else.
$items =
$this->where( 'active', 1 )
->where( function ($query) {
$query->whereIn( 'slug', $this->shared_slugs )
->orWhere(function ($query) {
$query->whereRaw( $this->threshold_rank.' <= '.$this->user_rank )
->whereIn('id', function ($query) {
$query->select('admin_item_id')->from('f_user_access')->where('user_id', $this->user_id)->get();
});
})
->orWhere(function ($query) {
$query->whereRaw( $this->threshold_rank.' > '.$this->user_rank )
->where( 'rank', '>=', $this->user_rank );
});
})->orderBy( 'order', 'ASC' )->get();
What I like about this second one is the fact I can pass $shared_slugs as an array, I don't have to convert it into a string first. But apart from that I really hate the looks of this, where(function($query){}) etc etc... On top of that the $ids passed to this function, are not accessible in the where.functions, so I had to define them in the class first.
The first one I like because of the named binding and it doesn't read that bad to be :S, also the vars are accessible.. Downside, I have to call this function to convert the shared slugs into an array.
Is it really that bad to not use eloquent and querybuilder? at least in some cases... and what would you guys do to make the second example better? =/
UPDATE::
Due to the answers and feedback, I ditched the raw sql. The current function has 5 scopes in it, and a (small) model is made for the user_access.
$items =
$this->active() //Only active items AND... 1, 2 or 3
->where( function ($query) {
$query->shared_slug() // 1
->orWhere(function ($query) { // OR 2
$query->access_required()
->whereIn('id', UserAccess::get_access( $this->user_id ) );
})
->orWhere(function ($query) { // OR 3
$query->no_access_required()
->whereRaw( 'rank >= '.$this->user_rank );
});
})->ASC()->get();
The major benefit of using the query builder is it abstracts you away from the language used by your storage of choice, i.e. MySQL, Oracle, SQLite, etc. If you ever switch database types, you can be stuck with a lot of refactoring raw SQL. Believe me, it's not pretty when you start on a project and learn this is the case.
There are always caveats, however, which is precisely why Eloquent has the capability to work with raw statements, as well.

Laravel query optimization

I have a query in laravel:
...
$query = $model::group_by($model->table().'.'.$model::$key);
$selects = array(DB::raw($model->table().'.'.$model::$key));
...
$rows = $query->distinct()->get($selects);
this works fine and gives me the fields keys' that I need but the problem is that I need to get all the columns and not just the Key.
using this:
$selects = array(DB::raw($model->table().'.'.$model::$key), DB::raw($model->table().'.*'));
is not an option, cuz it's not working with PostgreSQL, so i used $rows to get the rest of columns:
for ($i = 0; $i<count($rows); $i++)
{
$rows[$i] = $model::find($rows[$i]->key);
}
but as you see this is it's so inefficient, so what can i do to make it faster and more efficient?
you can find the whole code here: https://gist.github.com/neo13/5390091
ps. I whould use join but I don't know how?
Just don't pass anything in to get() and it will return all the columns. Also the key is presumably unique in the table so I don't exactly understand why you need to do the group by.
$models = $model::group_by( $model->table() . '.'. $model::$key )->get();

Entity Framework query

I have a piece of code that I don't know how to improve it.
I have two entities: EntityP and EntityC.
EntityP is the parent of EntityC. It is 1 to many relationship.
EntityP has a property depending on a property of all its attached EntityC.
I need to load a list of EntityP with the property set correctly. So I wrote a piece of code to get the EntityP List first.It's called entityP_List. Then as I wrote below, I loop through the entityP_List and for each of them, I query the database with a "any" function which will eventually be translated to "NOT EXIST" sql query. The reason I use this is that I don't want to load all the attached entityC from database to memory, because I only need the aggregation value of their property. But the problem here is, the looping will query the databae many times, for each EntityP!
So I am wondering if anybody can help me improve the code to query the database only once to get all the EntityP.IsAll_C_Complete set, without load EntityC to memory.
foreach(EntityP p in entityP_List)
{
isAnyNotComoplete = entities.entityC.Any(c => c.IsComplete==false && c.parent.ID == p.ID);
p.IsAll_C_Complete = !isAnyNotComoplete;
}
Thank you very much!
In EF 4, you can do:
var ids = entityP_List.Select(p => p.ID);
var q = (from p in entities.entityP
where ids.Contains(p => p.ID)
select new
{
ID = p.ID,
IsAll_C_Complete = !p.entityCs.Any(c => !c.IsComplete)
}).ToList();
foreach (var p in entityP_List)
{
p.IsAll_C_Complete = q.Where(e.ID == p.Id).Single().IsAll_C_Complete;
}
...which will do the whole thing in one DB query. For EF 1, Google BuildContainsExpression for a replacement for the .Contains( part of the above.
I would base EntityP on a SQL View instead of a table. Then I would define the relationship, and aggregate the value for child table within the view.

Resources