Running 2 queries in model (codeigniter)? - php

For example, if I run one query in model:
public function list_users() {
$q = "SELECT user_id, username FROM users";
return $q->result_array();
}
And now, to lists posts from that user, I need to refer to it's id within this function:
public function list_posts() {
$q = "SELECT post_id, post_title, post_content FROM posts
WHERE user_id = what??";
return $q->result_array();
}
OK both of these functions are in Model. Now, How to use RESULT from list_users() in list_posts(). Please have in mind that I need to pass ARRAY of IDs and, to use it only for particular id from list_users() which also returns ARRAY
I know I can use joined query, but that's not the point at all, as I have lots of queries that I need to split

why arent you using a JOIN statement, and making two queries into one?
This will reduce db load, decrease query times, and also reduce clutter in your models.
SELECT p.post_id, p.post_title, p.post_content, u.user_id, u.username FROM posts p LEFT JOIN users u ON u.user_id = p.user_id
you can also do this using active records. Which will avoid having to use full blown queries, and more of a CI methodology to SQL.
http://codeigniter.com/user_guide/database/active_record.html
$this->db->select('p.post_id, p.post_title, p.post_content, u.user_id, u.username');
$this->db->from('posts p');
$this->db->join('users u', 'u.user_id = p.user_id');
$q = $this->db->get();
$q->result();
Edit:
You can return the value as an object.. IE: $this->user_id then reference it in the posts function. Ideally you should call the first function in your Controller, return $user_id and then reference that in your next function.. This is definitely not best case though, you should use JOINs as they are less taxing on the db.
//controller
function test(){
$users = $this->exampleModel->list_users();
//manipulate user data if needed
$posts = $this->exampleModel->list_posts($users);
}

Related

Where should I place complex queries in Laravel?

So I've searched the internet for similar cases, and I just got lost from all contradicting answers, and unrelated scenarios. So I thought to put my case hoping to get some specific answers.
I am new to Laravel, and creating small application. In the application I have to search for offeres and show the result in a blade view. Since the query is complex, and output of the search does not belong to a specific Model, I just wish to keep it as a raw query.
I have placed the query in the controller, but I just don't feel it's the right place. Especially if I need to reuse the query in many places.
Here's the method from the OfferController:
public function search(Request $request)
{
$area = $request->area;
$size = $request->size;
$sql = "SELECT distinct product_name,product_offer.quantity, product_offer.price
FROM product
inner join brand on product.brand_id = brand.brand_id
inner join brand_area on brand_area.brand_id = brand.brand_id
inner join area on area.area_id = brand_area.area_id
inner join product_offer on product_offer.product_id = product.product_id
where area.area_id = :area
and product.size_id = :size ";
$params = array(
'area'=>$area,
'size'=>$size
);
$offers = DB::select( DB::raw($sql), $params);
return view('searchresult')->with($offers);
}
So in short: should I move the query to the model, create DAL class, or keep it here? Baring in mind the project is small scale.
in my opinion, if you are going to reuse it, create a service that will perform that query and gives you back a result, something like SearchService that looks like this:
<?php
class SearchService
{
public static function perform(array $params){
$sql = "SELECT distinct product_name,product_offer.quantity, product_offer.price
FROM product
inner join brand on product.brand_id = brand.brand_id
inner join brand_area on brand_area.brand_id = brand.brand_id
inner join area on area.area_id = brand_area.area_id
inner join product_offer on product_offer.product_id = product.product_id
where area.area_id = :area
and product.size_id = :size ";
return DB::select( DB::raw($sql), $params);
}
}
?>
And by doing so, you can just call
SearchService::perform([...]);
to get the results.
Obviously this is version1.0, you can improve it in a lot of ways, for example making it not static in order to make it testable, and also to allow getter and setter to exists, and a lot of other things, that might be usefull
You have a fair point saying it does not look right to place query in the controller. I would offer you to have a look at the concept of Laravel repository pattern:
https://dev.to/asperbrothers/laravel-repository-pattern-how-to-use-why-it-matters-1g9d
Also, I think you could use Laravel DB for this kind of query without the need to write it as a raw query. I do not think there is a huge need to have a raw query here. Laravel DB table(), select(), where() and other methods should be enough.
Actually, you could potentially write this using models and their relationships, but if the query is quite slow, it is better to use query builder for better efficiency.
EDIT:
also for some specific queries which do not belong to anything I remember seeing custom trait used, could also be a solution.
I'm writing my previous comment as an answer since I think its really what you are looking for:
I suggest you use a Trait to do this. The reason why is that it will keep your code clean and you'll be able to reuse this code later for other uses. A Trait is made for reusable code.
You will create a Trait:
<?php
namespace App\Traits; // Optional, but you could create a namespace for all your Traits if you need others
trait MyCustomTrait
{
public function perform(array $params) {
$sql = "SELECT distinct product_name,product_offer.quantity, product_offer.price
FROM product
inner join brand on product.brand_id = brand.brand_id
inner join brand_area on brand_area.brand_id = brand.brand_id
inner join area on area.area_id = brand_area.area_id
inner join product_offer on product_offer.product_id = product.product_id
where area.area_id = :area
and product.size_id = :size ";
return DB::select( DB::raw($sql), $params);
}
}
To use it, only write: use MyCustomTrait in the scope of your controller and call your function like this: $this->perform([...]).
Here's some links to learn more about traits:
https://www.w3schools.com/php/php_oop_traits.asp
https://www.develodesign.co.uk/news/laravel-traits-what-are-traits-and-how-to-create-a-laravel-trait/.
Hope it helps you!

PHP - 2 MySQL Queries in One PHP Function

I have two functions that I am using 2 different queries. I need to make one function that will retrieve the number of views for an item. Below are the two functions:
public function products_views(){
$this->db->select('*');
$this->db->from('products');
$this->db->order_by('view_count', 'DESC');
$query = $this->db->get();
return $query->result();
}
public function getViewCount($product_id) {
$this->db->select('COUNT(*) AS cnt');
$this->db->from(views);
$this->db->where('product_id', $product_id);
$query = $this->db->get();
return $query->row()->cnt;
}
I want a query that will return all the total view count for each product from the views table and display all the products from the products table showing the total view count.
You need to use JOIN for tables products and views connecting products.id with views.product_id. As a result, the request should look like this:
SELECT products.id, COUNT(views.id) as cnt
FROM views JOIN product ON products.id = views.product_id
GROUP BY views.product_id
You need to interpret this request using your active records.

get user with most articles and its amount with active record or query

I want to get the user that wrote the most articles. I do so fine in two ways with ActiveRecord like the following:
$table = Articles::find()
->select('articles.*, COUNT(*) AS cnt')
->with('user','userDetails')
->groupBy('articles.user_id')
->orderBy(('cnt DESC'))
->limit(10)
->offset($offset)
->all();
and with a query like the following:
$query = (new Query())
->select('articles.user_id, COUNT(*) AS num_articles')
->from('articles')
->join('LEFT JOIN', 'user_details', 'user_details.user_id = articles.user_id')
->groupBy('articles.user_id')
->orderBy('num_articles DESC')
->limit(10)
->offset($offset)
->all();
The problem is that the ActiveRecord gives me further needed informations userDetails that I need. But I do not get the amount of articles of user that should be on cnt
With the Query I get the user_id and the amount of articles. But I do not get it working by joining with userDetails. All of these does not work: LEFT JOIN, RIGHT JOIN, INNER JOIN.
I am interested in resolving both for learning, but for concrete I need help with the ActiveRecord problem.
Okay well I solved it for the ActiveRecord. The ActiveRecords needs a public $var; in the Model. So to get the amount you have to add the mentioned public... to your Model so that in my case:
public $cnt; extends the ActiveRecord of Articles
now I can access it with the given Request in my Question. But this just solves the first point. I am still interested in the second way for more complex Queries.
I dont have much idea about active record but I think the below is something what you are looking for
select * from user_details where user_id in
(select A.user from
(select user_id as user, COUNT(*) AS num_articles
from articles group by user_id order by num_articles desc LIMIT 10)A
);
for second point you should include required column from joined table to select statement:
$query = (new Query())
->select('articles.user_id, COUNT(*) AS num_articles, user_details.username as username')
->from('articles')
->join('LEFT JOIN', 'user_details', 'user_details.user_id = articles.user_id')
->groupBy('articles.user_id')
->orderBy('num_articles DESC')
->limit(10)
->offset($offset)
->all();

How to select join multiple table without conditional in Codeigniter?

I want to select multiple table using join in codeigniter but I don't want to make any conditional as below code
public function get_invoice(){
$this->db->select('isp.*,dp.*,ip.*');
$this->db->from('isp');
$this->db->join('dp','dp.status = isp.status');
$this->db->join('isp','isp.status = ip.status');
$this->db->limit(5000);
$this->query = $this->db->get();
if($this->query->num_rows()>0){
return $this->query->result();
}
}
however I want to get all data in that table without conditional but in join statement have to used conditional.
So how can I select data from multiple table without conditional in codeigniter?
Thanks for help
Yes you can join query without condition. INNER LEFT RIGHT JOINS need condition.So you can do Cross JOIN.LIKE this
public function get_invoice(){
$this->db->select('isp.*,dp.*,ip.*');
$this->db->from('isp,dp,ip');
$this->db->limit(5000);
$this->query = $this->db->get();
if($this->query->num_rows()>0)
{
return $this->query->result();
}
}
But at Cross JOIN if your isp has 100,dp has 20, and ip has 50 record It will produce (100*20*50) record.I hope you know what CROSS,LEFT,INNER,RIGHT JOIN do.

Laravel many to many loading related models with count

I am trying to link 4 tables and also add a custom field calculated by counting the ids of some related tables using laravel.
I have this in SQL which does what I want, but I think it can be made more efficient:
DB::select('SELECT
posts.*,
users.id AS users_id, users.email,users.username,
GROUP_CONCAT(tags.tag ORDER BY posts_tags.id) AS tags,
COUNT(DISTINCT comments.id) AS NumComments,
COUNT(DISTINCT vote.id) AS NumVotes
FROM
posts
LEFT JOIN comments ON comments.posts_id = posts.id
LEFT JOIN users ON users.id = posts.author_id
LEFT JOIN vote ON vote.posts_id = posts.id
LEFT JOIN posts_tags ON posts_tags.posts_id = posts.id
LEFT JOIN tags ON tags.id = posts_tags.tags_id
GROUP BY
posts.id,
posts.post_title');
I tried to implement it using eloquent by doing this:
$trending=Posts::with(array('comments' => function($query)
{
$query->select(DB::raw('COUNT(DISTINCT comments.id) AS NumComments'));
},'user','vote','tags'))->get();
However the NumComments value is not showing up in the query results.
Any clue how else to go about it?
You can't do that using with, because it executes separate query.
What you need is simple join. Just translate the query you have to something like:
Posts::join('comments as c', 'posts.id', '=', 'c.id')
->selectRaw('posts.*, count(distinct c.id) as numComments')
->groupBy('posts.id', 'posts.post_title')
->with('user', 'vote', 'tags')
->get();
then each post in the collection will have count attribute:
$post->numComments;
However you can make it easier with relations like below:
Though first solution is better in terms of performance (might not be noticeable unless you have big data)
// helper relation
public function commentsCount()
{
return $this->hasOne('Comment')->selectRaw('posts_id, count(*) as aggregate')->groupBy('posts_id');
}
// accessor for convenience
public function getCommentsCountAttribute()
{
// if relation not loaded already, let's load it now
if ( ! array_key_exists('commentsCount', $this->relations)) $this->load('commentsCount');
return $this->getRelation('commentsCount')->aggregate;
}
This will allow you to do this:
$posts = Posts::with('commentsCount', 'tags', ....)->get();
// then each post:
$post->commentsCount;
And for many to many relations:
public function tagsCount()
{
return $this->belongsToMany('Tag')->selectRaw('count(tags.id) as aggregate')->groupBy('pivot_posts_id');
}
public function getTagsCountAttribute()
{
if ( ! array_key_exists('tagsCount', $this->relations)) $this->load('tagsCount');
$related = $this->getRelation('tagsCount')->first();
return ($related) ? $related->aggregate : 0;
}
More examples like this can be found here http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/
as of laravel 5.3 you can do this
withCount('comments','tags');
and call it like this
$post->comments_count;
laravel 5.3 added withCount

Categories