Laravel: user defined object name in Join Query - php

I have a successfully returning join query written in Laravel. I am now trying to add an object name to the returned value as if it is returned from a table.
Laravel Query
$post = DB::table('follow')
->join('posts', 'follow.user2', '=', 'posts.userid')
->where('follow.user1',Auth::user()->id)
->where('follow.user2','!=',Auth::user()->id)
->where('posts.created_at','>',$update)
->select('posts.created_at', 'posts.userid')
->orderBy('posts.created_at','desc')
->get();
The above query return the following
array (size=1)
0 =>
object(stdClass)[208]
public 'created_at' => int 1418466963
public 'userid' => int 5
What I want to achieve is the following output
array (size=1)
0 =>
object(stdClass)[208]
public 'created_at' => int 1418466963
public 'userid' => int 5
public 'oType' => string 'post' //This is user defined.
What I tried is (Obviously wrong but just a hint of what am I trying)
$post = DB::table('follow')
->join('posts', 'follow.user2', '=', 'posts.userid')
->where('follow.user1',Auth::user()->id)
->where('follow.user2','!=',Auth::user()->id)
->where('posts.created_at','>',$update)
->select('posts.created_at', 'posts.userid', 'oType as post') //Compare this line with 1st query
->orderBy('posts.created_at','desc')
->get();

You can achieve this by using a DB::raw().
->select('posts.created_at', 'posts.userid', DB::raw('\'post\' as oType'))

Related

Codeigniter WHERE clause issue using NULL

Im trying to return records WHERE an individuals phone number is either empty as in '' or NULL. Please check this code out
$this->db->select('i.id, i.name, i.phone, b.date')
->from('bookings AS b')
->join('individuals AS i', 'b.individual_id = i.id')
->where('b.status', 'confirmed')
->where('b.date >=', '2022-09-12')
->where('b.date <=', '2023-01-15')
->where('i.phone = "" OR i.phone IS NULL')
->order_by('b.date', 'ASC')
->get();
The result is it brings back NULL values for Phone but the date ranges in the where clause get overridden and the value of that field is also NULL. Think im missing something just not sure what.
It brings back this
object(stdClass)[129]
public 'id' => string '52393' (length=5)
public 'name' => string 'Mrs Janet dooley' (length=17)
public 'phone' => null
public 'date' => null
1 =>
object(stdClass)[198]
public 'id' => string '32277' (length=5)
public 'name' => string 'Ms Rita molongi' (length=16)
public 'phone' => null
public 'date' => null
->where('i.phone = "" OR i.phone IS NULL')
Kindly remove the condition (i.phone = "" OR) from your query and verify the outcome. There is no need to include an empty condition in the query.
Please let me know if you are unable to obtain the result and I will assist you in resolving the issue.
Also you can use ->where('i.phone',NULL);
Your 'OR' is breaking the query as CI would just print it out with no breaks between the ANDs and ORs. Do a echo $this->db->last_query(); to confirm. The answer is to parenthesis it:
where('(i.phone = "" OR i.phone IS NULL)')
You can also do a group_start but that depends on your CI version.
Grouping WHERE clauses in Codeigniter

Dynamically add columns to query results via CakePHP 3 ORM queries

I'm trying to write a query using CakePHP 3.7 ORM where it needs to add a column to the result set. I know in MySQL this sort of thing is possible: MySQL: Dynamically add columns to query results
So far I've implemented 2 custom finders. The first is as follows:
// src/Model/Table/SubstancesTable.php
public function findDistinctSubstancesByOrganisation(Query $query, array $options)
{
$o_id = $options['o_id'];
$query = $this
->find()
->select('id')
->distinct('id')
->contain('TblOrganisationSubstances')
->where([
'TblOrganisationSubstances.o_id' => $o_id,
'TblOrganisationSubstances.app_id IS NOT' => null
])
->orderAsc('Substances.app_id')
->enableHydration(false);
return $query;
}
The second custom finder:
// src/Model/Table/RevisionSubstancesTable.php
public function findProductNotifications(Query $query, array $options)
{
$date_start = $options['date_start'];
$date_end = $options['date_end'];
$query = $this
->find()
->where([
'RevisionSubstances.date >= ' => $date_start,
'RevisionSubstances.date <= ' => $date_end
])
->contain('Substances')
->enableHydration(false);
return $query;
}
I'm using the finders inside a Controller to test it out:
$Substances = TableRegistry::getTableLocator()->get('Substances');
$RevisionSubstances = TableRegistry::getTableLocator()->get('RevisionSubstances');
$dates = // method to get an array which has keys 'date_start' and 'date_end' used later.
$org_substances = $Substances->find('distinctSubstancesByOrganisation', ['o_id' => 123);
if (!$org_substances->isEmpty()) {
$data = $RevisionSubstances
->find('productNotifications', [
'date_start' => $dates['date_start'],
'date_end' => $dates['date_end']
])
->where([
'RevisionSubstances.substance_id IN' => $org_substances
])
->orderDesc('RevisionSubstances.date');
debug($data->toArray());
}
The logic behind this is that I'm using the first custom finder to produce a Query Object which contains unique (DISTINCT in SQL) id fields from the substances table, based on a particular company (denoted by the o_id field). These are then fed into the second custom finder by implementing where(['RevisionSubstances.substance_id IN' ....
This works and gives me all the correct data. An example of the output from the debug() statement is as follows:
(int) 0 => [
'id' => (int) 281369,
'substance_id' => (int) 1,
'date' => object(Cake\I18n\FrozenDate) {
'time' => '2019-09-02T00:00:00+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'comment' => 'foo',
'substance' => [
'id' => (int) 1,
'app_id' => 'ID000001',
'name' => 'bar',
'date' => object(Cake\I18n\FrozenDate) {
'time' => '2019-07-19T00:00:00+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
}
]
],
The problem I'm having is as follows: Each of the results returned contains a app_id field (['substance']['app_id'] in the array above). What I need to do is perform a count (COUNT() in MySQL) on another table based on this, and then add that to the result set.
I'm unsure how to do this for a couple of reasons. Firstly, my understanding is that custom finders return Query Objects, but the query is not executed at this point. Because I haven't executed the query - until calling $data->toArray() - I'm unsure how I would refer to the app_id in a way where it could be referenced per row?
The equivalent SQL that would give me the required results is this:
SELECT COUNT (myalias.app_id) FROM (
SELECT
DISTINCT (tbl_item.i_id),
tbl_item.i_name,
tbl_item.i_code,
tbl_organisation_substances.o_id,
tbl_organisation_substances.o_sub_id,
tbl_organisation_substances.app_id,
tbl_organisation_substances.os_name
FROM
tbl_organisation_substances
JOIN tbl_item_substances
ON tbl_organisation_substances.o_sub_id = tbl_item_substances.o_sub_id
JOIN tbl_item
ON tbl_item.i_id = tbl_item_substances.i_id
WHERE
tbl_item.o_id = 1
AND
tbl_item.date_valid_to IS NULL
AND
tbl_organisation_substances.app_id IS NOT NULL
ORDER BY
tbl_organisation_substances.app_id ASC
) AS myalias
WHERE myalias.app_id = 'ID000001'
This does a COUNT() where the app_id is ID000001.
So in the array I've given previously I need to add something to the array to hold this, e.g.
'substance' => [
// ...
],
'count_app_ids' => 5
(Assuming there were 5 rows returned by the query above).
I have Table classes for all of the tables referred to in the above query.
So my question is, how do you write this using the ORM, and add the result back to the result set before the query is executed?
Is this even possible? The only other solution I can think of is to write the data (from the query I have that works) to a temporary table and then perform successive queries which UPDATE with the count figure based on the app_id. But I'm really not keen on that solution because there are potentially huge performance problems of doing this. Furthermore I'd like to be able to paginate my query so ideally need everything confined to 1 SQL statement, even if it's done across multiple finders.
I've tagged this with MySQL as well as CakePHP because I'm not even sure if this is achievable from a MySQL perspective although it does look on the linked SO post like it can be done? This has the added complexity of having to write the equivalent query using Cake's ORM.

"Trying to get property of non-object" Inner Join using PDO

i try show data using PDO. But i get Error "Trying to get property of non-object".
i have a simple script.
public function tampilUserId($user_id)
{
$sql = "SELECT $this->user.*, $this->provinsi.*
FROM $this->user
INNER JOIN $this->provinsi
ON $this->user.provinsi_id=$this->provinsi.provinsi_id
WHERE user_id=:user_id";
$stmt = db::prepare($sql);
$stmt->bindParam(':user_id', $user_id);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_OBJ);
}
And this
echo $results->email_user;
print_r($result);
result
Notice: Trying to get property of non-object in C:\xampp\htdocs\laporan_app\user_views\profile.php on line 35
stdClass Object ( [user_id] => 45 [nama_dpn_user] => [nama_blkng_user] => [username_user] => adi [password_user] => $2y$10$p/8gF5BcQSooQUKRlEAiPuOSy4o1RMeXA5Ul8GTZNYZi/4wcOP3Ja [email_user] => adi#gmail.com [level_user] => mahasiswa [img_user] => [_dir_img_user] => [_size_img_user] => [provinsi_id] => [universitas_id] => )
And i try this script
echo $results['email_user'];
print_r($results);
result
Notice: Undefined index: email_user in C:\xampp\htdocs\laporan_app\user_views\profile.php on line 35
stdClass Object ( [user_id] => 45 [nama_dpn_user] => [nama_blkng_user] => [username_user] => adi [password_user] => $2y$10$p/8gF5BcQSooQUKRlEAiPuOSy4o1RMeXA5Ul8GTZNYZi/4wcOP3Ja [email_user] => adi#gmail.com [level_user] => mahasiswa [img_user] => [_dir_img_user] => [_size_img_user] => [provinsi_id] => [universitas_id] => )
Please help me, thanks before.
Merdeka! :D
In case your SQL query not contain any error its mean data was successfully pulled from database. So in your tampilUserId($user_id) function, its returning set of array from $stmt->fetchAll(PDO::FETCH_OBJ). Like below:
/* Sample from Sakila database */
array (size=603)
0 =>
object(stdClass)[11]
public 'address_id' => string '1' (length=1)
public 'address' => string '47 MySakila Drive' (length=17)
public 'address2' => null
public 'district' => string 'Alberta' (length=7)
public 'city_id' => string '300' (length=3)
public 'postal_code' => string '' (length=0)
public 'phone' => string '' (length=0)
public 'last_update' => string '2014-09-25 22:30:27' (length=19)
1 =>
object(stdClass)[12]
public 'address_id' => string '2' (length=1)
public 'address' => string '28 MySQL Boulevard' (length=18)
public 'address2' => null
public 'district' => string 'QLD' (length=3)
public 'city_id' => string '576' (length=3)
public 'postal_code' => string '' (length=0)
public 'phone' => string '' (length=0)
public 'last_update' => string '2014-09-25 22:30:09' (length=19)
more elements...
All you need to do is looping your function returned value first whereever you call it. Short example from your case:
//I dont know you put it under class or not.
$data = $YourClass->tampilUserId($user_id);
foreach ($data as $item) {
echo $item->email_user);
}
Note:
You can manually echoing your data without looping and its bad practice because you dont know the length of the array. So this is just gusessing. Taken from your case it would be.
$data = $YourClass->tampilUserId($user_id);
$data[0]->email_user; //If an object
$data[0]['email_user']; //If an array
Update:
There is suspicious thing on your query. The variable $this->user is not look like column name to me(unsure) If yes, so you have small mistake on your query. Secondly, as you said the result is null its mean something wrong or data you search not exist. I put this sample running inner join query.
Example:
Lets said i had 2 table. 1st is address and 2nd is city
|address table|
|city table|
So your sql query for joining table(from image above) should be:
SELECT adr.address_id, adr.address, adr.district, adr.city_id, c.city_id,
c.city FROM address AS adr INNER JOIN city AS c ON adr.city_id = c.city_id
WHERE c.city_id = 300 #using city id
Output:
Bring it to PHP
<?php
$pdo= new PDO('mysql:dbname=sakila;host=localhost:3306', 'user', password');
$city_id = 300; // manual set
$sql = 'SELECT adr.address_id, adr.address, adr.district, adr.city_id,
c.city_id, c.city FROM address AS adr INNER JOIN city AS c ON
adr.city_id = c.city_id WHERE c.city_id = :city_id';
$stmt = $_this->db->prepare($sql);
$stmt->bindParam(':city_id', $city_id);
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_OBJ);
?>
var_dump($data) giving a result:
Great, as it come as (object(stdClass)) so we can loop it through the array & then accessing it using -> sign.
<?php
$i = 1;
foreach ($data as $d) {
echo '<dl>';
echo '<dt>Adress '.$i++.':</dt>';
echo '<li>Address id: '.$d->address_id.'</li>';
echo '<li>Address: '.$d->address.'</li>';
echo '<li>District: '.$d->district.'</li>';
echo '<li>City id: '.$d->city_id.'</li>';
echo '<li>City name: '.$d->city.'</li>';
echo '</dl>';
}
?>
Result in browser:
Hope this will resolve your problem.

Join multiple left values with one right value

In Codeigniter I am tryign to join two tables with one to many relation. I want to get one result from my table housetype and all of its values/members from other table housetype_member:
$this->db->select('*');
$this->db->join('housetype_member', 'housetype_member.housetype_id = housetype.PkId', 'left');
$result = $this->db->get_where('housetype', array('PkId' => $id));
return $result->result();
So far I get such result:
array (size=2)
0 =>
object(stdClass)[28]
public 'PkID' => string '4' (length=1)
public 'Name' => string 'Classic' (length=7)
public 'image' => string '1449063250.jpg' (length=14)
1 =>
object(stdClass)[30]
public 'PkID' => string '4' (length=1)
public 'Name' => string 'Classic' (length=7)
public 'image' => string '1449063288.gif' (length=14)
First two object values (PkID, Name) are from the first table and the last one (image) is from the second left table. Everything is good but I get array with two elements, when I only need one housetype object.
Is there a way to write above code so that my returned object would look like this:
object(stdClass)[28]
public 'PkID' => string '4' (length=1)
public 'Name' => string 'Classic' (length=7)
public 'image' =>
array (size=2)
0 => string '1449063250.jpg' (length=14)
1 => string '1449063288.gif' (length=14)
I need one result from first table and to it I want to join all of its members from the second table.
Can it be done with Codeigniters active record?
As far as your second table has multi records with that primary key, it is better if you don't use joins at all.
You can simply get that with two selects.
$this->db->select('PkID, name');
$this->db->where('PkId', $id);
$houseTypes = $this->db->get('housetype')->result();
foreach($houseTypes as $houseType){
$this->db->select('image');
$this->db->where('housetype_id', $houseType->PKId);
$houseType->image = $this->db->get('housetype_member')->result();
}
return $houseTypes;

Why laravel query doesn't return right result?

I have this code in laravel to get the products that will run out soon.
$productos = DB::table('productos')
->where('producto_minimo', '>=', 'producto_cantidad')
->get();
And what I get is the following result
which is not the right result. While in MySql I get the right results whith this query SELECT * FROM productos where producto_minimo >= producto_cantidad;
Update
The query log - DB::getQueryLog() - shows this
2 =>
array (size=3)
'query' => string 'select * from `productos` where `producto_minimo` >= ?' (length=54)
'bindings' =>
array (size=1)
0 => string 'producto_cantidad' (length=17)
'time' => float 1
I assume you've got to use the whereRaw method:
$productos = DB::table('productos')
->whereRaw('producto_minimo >= producto_cantidad')
->get();
Your query will compare the value in the column producto_minimo with the string 'producto_cantidad'
Have a look at Eloquents documentation of advanced wheres:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
The query above will produce the following SQL:
select * from users
where exists (
select 1 from orders where orders.user_id = users.id
)

Categories