symfony querybuilder and increase of sql request number - php

I made a querybuilder to get all the entities that I want into one request but at the end, it makes 26 sql requests.
Is there a way to do it?
Thank you for any response/help.
Here is the query builder:
$builder = $this->createQueryBuilder("message")
->innerJoin("AppBundle:User", "user", "WITH", "message.sender = user.id")
->innerJoin("AppBundle:Thread", "thread", "WITH", "message.thread = thread.id")
->innerJoin("AppBundle:MessageMetadata", "messageMetadata", "WITH", "messageMetadata.message = message.id AND messageMetadata.participant != user.id")
;
$builder = $this->filterSoftdelete($builder, $user);
if($filters != null) {
foreach ($filters as $key => $value) {
$builder->andWhere("(message.recipient = :id AND user.firstname LIKE '%". $value ."%') OR
(message.recipient = :id AND user.lastname LIKE '%". $value ."%') OR
(message.recipient = :id AND thread.subject LIKE '%". $value ."%')")
->setParameter("id", $user->getId());;
}
} else {
$builder->andWhere("message.recipient = :id or message.sender = :id")
->setParameter("id", $user->getId());
}
if($ordering != null) {
foreach ($ordering as $key => $value) {
if($key == "subject") {
$builder->addOrderBy("thread.subject", $value);
} else if($key == "createdAt") {
$builder->addOrderBy("message." . $key, $value);
} else {
$builder->addOrderBy("messageMetadata.isRead", "desc");
}
}
} else {
$builder->addOrderBy("messageMetadata.isRead", "desc");
}
return $this->query($builder);

Your builder should only fire one request. But maybe are you trying to access attribute or joined collections that are not fetched and lazy loading comes into play thus creating many unwanted requests.
Typically when in controller you fetch your entities using your builder :
$entities = $em->getRepository('myrepo')->myCustomQuery($myparams...);
Then you loop through your entities (maybe in twig) :
foreach ($entities as $entity) {
$entity->getAssociation()->doSomething; // sometime here you forgot to fetch sayed association
}
The solution would be to use ->addSelect() in your query builder, which will force the selection and hydration of associated entities.
Basically you're doing :
SELECT entity.* FROM entity JOIN other_entity ON ...;
And doctrine, when needed fetch the missing other_entity resulting in unwanted queries.
But you should be doing :
SELECT entity.*, other_entity.* FROM entity JOIN other_entity ON ...;
You should use symfony toolbar to look at your request.
Hope this helps

Related

Join 3 tables codeigniter php

I have 3 tables guest_user_info,pg_company,user_profile.
In guest_user_info have 2 columns:
g_uid | company_id
In pg_company have 2 columns:
company_id | user_id
In user_profile have 2 columns:
id |user_email
Here i want to get user_email from user_profile.i have g_uid value (in guest_user_info table).i want company_id from guest_user_info and get the company_id and match with pg_company table,there i can get user_id.then match with that user_id with id in user_profile table.at last i need user_email from user_profile table
Well its a simple one, you just need to join using active query in CodeIgniter.
$this->db->select("UP.id", "UP.user_email");
$this->db->from("guest_user_info AS GU");
$this->db->join("pg_company AS PC", "PC.company_id=GU.company_id");
$this->db->join("user_profile AS UP", "UP.id=PC.user_id");
$this->db->where("GU.g_uid", $guid);
$query = $this->db->get();
return $query->result();
In above code, $guid you have to provide which you have.
Also please take a look at these links:
https://www.codeigniter.com/userguide3/database/query_builder.html
https://www.codeigniter.com/userguide2/database/active_record.html
You get so many things after reading this.
Check bellow code it`s working fine and common model function also
supported more then one join and also supported multiple where condition
order by ,limit.it`s EASY TO USE and REMOVE CODE REDUNDANCY.
================================================================
*Album.php
//put bellow code in your controller
=================================================================
$album_id='';//album id
//pass join table value in bellow format
$join_str[0]['table'] = 'pg_company';
$join_str[0]['join_table_id'] = 'pg_company.company_id';
$join_str[0]['from_table_id'] = 'guest_user_info.company_id';
$join_str[0]['join_type'] = '';//set join type
$join_str[1]['table'] = 'user_profile';
$join_str[1]['join_table_id'] = 'user_profile.id';
$join_str[1]['from_table_id'] = 'guest_user_info.user_id';
$join_str[1]['join_type'] = '';
$selected ="guest_user_info.*,user_profile.user_name,pg_company.name";
$condition_array=array('guest_user_info.g_uid' => $g_uid);
$albumData= $this->common->select_data_by_condition('guest_user_info', $condition _array, $selected, '', '', '', '', $join_str);
//call common model function
if (!empty($albumData)) {
print_r($albumData); // print album data
}
=========================================================================
Common.php
//put bellow code in your common model file
========================================================================
function select_data_by_condition($tablename, $condition_array = array(), $data = '*', $sortby = '', $orderby = '', $limit = '', $offset = '', $join_str = array()) {
$this->db->select($data);
//if join_str array is not empty then implement the join query
if (!empty($join_str)) {
foreach ($join_str as $join) {
if ($join['join_type'] == '') {
$this->db->join($join['table'], $join['join_table_id'] . '=' . $join['from_table_id']);
} else {
$this->db->join($join['table'], $join['join_table_id'] . '=' . $join['from_table_id'], $join['join_type']);
}
}
}
//condition array pass to where condition
$this->db->where($condition_array);
//Setting Limit for Paging
if ($limit != '' && $offset == 0) {
$this->db->limit($limit);
} else if ($limit != '' && $offset != 0) {
$this->db->limit($limit, $offset);
}
//order by query
if ($sortby != '' && $orderby != '') {
$this->db->order_by($sortby, $orderby);
}
$query = $this->db->get($tablename);
//if limit is empty then returns total count
if ($limit == '') {
$query->num_rows();
}
//if limit is not empty then return result array
return $query->result_array();
}

Doctrine DQL conditional query with Joins

I'm trying to build a dynamic query with Doctrine's query builder. I have optional parameters. How would I add this conditionally to the statement?I also have associations (eg, Service has many-to-one relation to Category)
$qb = $this->createQueryBuilder('service');
$qb->join('service.category', 'category');
$conditions = array();
if ($categoryId != null) {
$category = $this->getEntityManager()
->getRepository('AppBundle:Category')->find($categoryId);
if($category == null){
throw new ApiException('category not found');
}
$conditions[] = $qb->expr()->like('category.path', $category->getPath().'%');
}
if ($userId != null) {
$user = $this->getEntityManager()->getRepository('AppBundle:User')->find($userId);
if($user == null){
throw new ApiException('user not found');
}
$conditions[] = $qb->expr()->eq('service.user', $userId);
}
if ($rating != null) {
$conditions[] = $qb->expr()->gte('service.rating', $rating);
}
$conditions = call_user_func_array(array($qb->expr(), 'andX'), $conditions);
$qb->where($conditions);
$qb->addOrderBy('service.created', 'DESC');
return $qb;
}
When I try to send query
http://127.0.0.1:8000/api/services?limit=10&categoryId=45
I get following error:
Error: Expected StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression, got "45" (500 Internal Server Error)
Try with the Literal expression of the given argument. So try this:
$conditions[] = $qb->expr()->gte('service.rating', $this->expr()->literal($rating));
Instead of this:
$conditions[] = $qb->expr()->gte('service.rating', $rating);
Hope this help

Eloquent Query Dynamically based on json

I want to query dynamically based on payload(json) from database.
Example:
$data = [{"key":"age","relation":">","value":"15"},{"operator":"OR"},{"key":"age","relation":"<=","value":"20"}]
I want to do query based on that payload.
Right now what I'm doing is:
$query = User::all();
$payload = json_decode($data, true);
foreach($payload as $value){
if ($value['key'] == 'age') {
$query = $query->where('birthday', $value['relation'], Carbon::now()->subYears($value['age'])->format('Y-m-d');)
}
if($value['key'] == 'gender'{
$query = $query->where('gender', $value['relation'], $value['gender']);
}
}
The problem is yes it can work, but I don't think this is best approach. I don't get any solution to use the "operator" key. Operator usage is to change where to orWhere.
Any solution or tips to make it call dynamically like this?. I want my column at DB neat and simple. I can only think this way.
Thanks!
Encountering this problem, I would go with Local Query Scopes. In this approach You create a model method named scopeJson() or whatever you feel better with to handl all conditions inside. I tried to handle most conditions here not only single where and orWhere. I assumed that your payload contains only one builder at a time.
public function scopeJson($query, $json)
{
$wheres = [
'between' => ['whereBetween', 'not' => 'whereNotBetween'],
'null' => ['whereNull', 'not' => 'whereNotNull'],
'or' => ['orWhere', 'not' => 'orWhereNot'],
'in' => ['whereIn', 'not' => 'whereNotIn'],
'and' => ['where', 'not' => 'orWhereNot'],
'raw' => 'whereRaw'
];
$builder = json_decode($json);
if (count($builder) > 0) {
$query->where(
$builder[0]->key,
$builder[0]->relation,
$builder[0]->value
);
// notBetween, notNull, notOr, notIn, notAnd
if (stripos($builder[1]->operator, 'not') !== false) {
$whereCondition = $wheres[strtolower(substr($builder[1]->operator, 3))]['not'];
} else {
$whereCondition = $wheres[strtolower($builder[1]->operator)];
}
if (count($builder[2]) == 3) {
if ($whereCondition == 'whereRaw') {
$query->$whereCondition(implode(" ", $builder[2]));
} else {
// where, whereNot
$query->$whereCondition(
$builder[2]->key,
$builder[2]->relation,
$builder[2]->value
);
}
} elseif (count($builder[2]) == 2) {
// whereBetween, whereNotBetween, where, whereNot
$query->$whereCondition(
$builder[2]->key,
$builder[2]->value
);
} elseif (count($builder[2]) == 1) {
// whereNull, whereNotNull, whereRaw
$query->$whereCondition(
$builder[2]->key ?? $builder[2]->value // PHP 7.0 Null Coalescing Operator
);
}
}
return $query;
}
If this method is defined within your User's model then you can use it this way:
$users = User::json($data)->get();
PS: Although it should work, I didn't test it.
You can do raw query like this.
$data = '[{"key":"age","relation":">","value":"15"},{"operator":"OR"},{"key":"age","relation":"<=","value":"20"}]';
$query = "SELECT * FROM tablename WHERE";
$payload = json_decode($data, true);
foreach ($payload as $value) {
if (isset($value['operator'])) {
$query .= " " . $value['operator'];
} else {
if ($value['key'] == 'age') {
$query .= " 'birthday' " . $value['relation'] . " " . Carbon::now()->subYears($value['value'])->format('Y-m-d');
}
if ($value['key'] == 'gender') {
$query .= " 'gender' " . $value['relation'] . " " . $value['gender'];
}
}
}
This results in a query like this :
SELECT * FROM tablename WHERE 'birthday' > 2001-07-02 OR 'birthday' <= 1996-07-02
Of course, you might use printf() for formatting and making this cleaner in some other way but this will get you started hopefully.
You can use a variable function name to add your orWhere logic:
$query = User::all();
$payload = json_decode($data, true);
$function = 'where';
foreach($payload as $value){
if(isset($value['operator'])){
$function = $value['operator'] == 'OR' ? 'orWhere' : 'where';
} else {
if ($value['key'] == 'age') {
$query = $query->$function('birthday', $value['relation'], Carbon::now()->subYears($value['age'])->format('Y-m-d');)
} else {
$query = $query->$function($value['key'], $value['relation'], $value['value']);
}
}
}
As long as your json data doesn't match your database (ie. the json has age but the database has birthday) you will not be able to avoid having that if/else statement. That custom logic will have to remain.
Ultimately this idea is its own limiter because the stored queries will have to represent the current state of the database. This means that the maintenance cost of these queries will be significant the moment data is stored in a different way -- if you changed the column birthday to date_of birth all of your stored queries will break. Avoid this.
Instead this goal is better achieved by storing the queries on the Model using Query Scopes and Relationships. If you still need a dynamic list of requests you can store keywords that are associated with the queries and loop through them.

sort varchar column codeigniter

i first checked if there any same problems like mine i ddnt find anything.
all are sorting alphanumeric column mixed with numeric data.
here is my problem.
i have a table that contain column A datas like this.
WRG-01 WRG-39 WRG-22 WRG-45 WRG-43
need to sort that as
WRG-01 WRG-22 WRG-39 WRG-43 WRG-45
this is the code i using so far in codeigniter frame work
$data['products'] = $this->db->order_by('product_id', 'asc')->get('products');
in mysql i can use this query to get done my work
preg_replace("/[^\d]/", "",'product_id'), 'asc')
How to apply it to my above codeigniter code?
here is search funtion
public function search()
{
$data['title'] = 'Search Product';
$product_name = $this->input->get('product_name');
$product_id = $this->input->get('product_id');
$product_category = $this->input->get('product_category');
$secondCategory = $this->input->get('secondCategory');
$thirdCategory = $this->input->get('thirdCategory');
$data['category'] = $this->db->order_by('id', 'asc')->get_where('categories', ['parent' => 0]);
if($product_category != '')
{
$data['secondCategory'] = $this->db->get_where('categories', ['parent' => $product_category]);
}
if($secondCategory != '')
{
$data['thirdCategory'] = $this->db->get_where('categories', ['parent' => $secondCategory]);
}
if($product_name != '')
{
$this->db->like('product_name', $product_name);
}
if($product_id != '')
{
$this->db->where('product_id', $product_id);
}
if($product_category != '')
{
$this->db->where('product_category', $product_category);
}
if($secondCategory != '')
{
$this->db->where('secondCategory', $secondCategory);
}
if($thirdCategory != '')
{
$this->db->where('thirdCategory', $thirdCategory);
}
$data['products'] = $this->db->order_by('product_id' 'asc')->get('products');
theme('all_product', $data);
}
i can't use sql query here because products is result array from product table.
Use MySQL cast
cast(product_id as SIGNED)
or
cast(product_id as UNSIGNED)
Try query like that :-
select * from products cast(product_id as UNSIGNED) ASC|DESC
Try this
$query= $this->db->query("SELECT * FROM products WHERE ??==?? ORDER BY product_id ASC");
$result= $query->result_array();
return $result;
as default data will sort by Ascending Order
This is in model. So if you pass it to controller it will return data as Objective Array.
So in controller you can access
$result = $this->model_name->method_for_above_code();
$name = $result[0]['name'];
$id = $result[0]['id'];
if in View
$result['this_for_view'] = $this->model_name->method_for_above_code();
foreach ($this_for_view as $new_item) {
echo "Name is ".$new_item['name'];
echo "ID is ".$new_item['id'];
}

Concatenate queries using Eloquent Builder

How can I concatenate queries using Eloquent Builder?
I am building queries based on criteria (where clause) and taking limit and offset from URL. These queries are then passed to ->get() method to fetch result. I want to do it using Eloquent and not Query builder.
This is how you build a query in eloquent(I have given an example of using multiple where clauses):
$result = ModelName::where('key_1', '=' , 'value_1')
->where('key_2', '>', 'value_2')
->take(4)
->offset(2)
->get()
The take() method will limit the number of results to 4 with offset 2.
http://laravel.com/docs/5.0/eloquent
Update
Based on OP's question over here https://laracasts.com/discuss/channels/general-discussion/eloquent-query-builder , I am updating my answer.
You could do something like this:
if($params)
{
$query = $this->model;
foreach($params['search'] as $param)
{
$query = $query->where($param['where'],'=',$param['value']);
}
if (isset($params['start']))
{
$query = $query->offset($params['start'] );
}
if(isset($params['count']))
{
$query = $query->take($params['count']);
}
if (isset($params['sortColumn']))
{
$ascending = $params['ascending'] == 'true' ? 'ASC' : 'DESC';
$query = $query->orderBy($params['sortColumn'], $ascending);
}
}
$query->get();
What you need is assigning result of functions again to the model.
You had:
if($params)
{
foreach($params['search'] as $param)
{
$this->model->where($param['where'],'=',$param['value']);
}
if (isset($params['start']))
{
$this->model->offset($params['start'] );
}
if(isset($params['count']))
{
$this->model->take($params['count']);
}
if (isset($params['sortColumn']))
{
$ascending = $params['ascending'] == 'true' ? 'ASC' : 'DESC';
$this->model->orderBy($params['sortColumn'], $ascending);
}
}
$this->model->get();
and you need to use:
if($params)
{
foreach($params['search'] as $param)
{
$this->model = $this->model->where($param['where'],'=',$param['value']);
}
if (isset($params['start']))
{
$this->model = $this->model->offset($params['start'] );
}
if(isset($params['count']))
{
$this->model = $this->model->take($params['count']);
}
if (isset($params['sortColumn']))
{
$ascending = $params['ascending'] == 'true' ? 'ASC' : 'DESC';
$this->model = $this->model->orderBy($params['sortColumn'], $ascending);
}
}
$data = $this->model->get();

Categories