Eloquent join with scopes having ambiguous column names - mysql

I've got two tables, venues and venue_plans, having a hasOne relationship. The former contains geolocation data, while the latter contains a "distance bonus", so the more the distance bonus, the closer the venue will be shown to the user who's searching around its location.
There's also a third table, categories, which has a hasMany relationship. Basically, each venue can have more than one categories set, as simple as that.
I have managed to add to scopes: the first one calculates the distance from a given location (for example, the user location), calculates the distance bonus and sort the venues by both fields:
public function scopeWithDistanceFrom($query, $lat, $lng, $units = 'km') {
$units = ($units === "km") ? 6378.10 : 3963.17;
$lat = (float) $lat;
$lng = (float) $lng;
$lat_column = 'geo_latitude';
$lng_column = 'geo_longitude';
// Join with venue_plans to get the distance bonus
$query->leftJoin('venue_plans', 'venues.id', 'venue_plans.venue_id');
// Add distance column
$distance_raw = "$units * ACOS(
COS(RADIANS($lat)) * COS(RADIANS($lat_column))
* COS(RADIANS($lng) - RADIANS($lng_column))
+ SIN(RADIANS($lat)) * SIN(RADIANS($lat_column))
) AS distance";
$query->selectRaw($distance_raw);
// Add distance_bonused column
$distance_bonused_raw = "(SELECT (distance - (distance / 100 * distance_bonus))) as distance_bonused";
$query->selectRaw($distance_bonused_raw);
// Sort by distance
$query->orderBy('distance_bonused', 'desc');
$query->orderBy('distance', 'desc');
return $query;
}
The second one searches venues by their name (venue.name) or by the category name (categories.name).
public function scopeWithNameOrCategoryName($query, $name) {
return $query
->where('name', 'like', "%{$name}%") // Venue name
->orWhereHas('categories', function($query) use ($name){ // Category name
$query->where('name', 'like', "%{$name}%");
});
}
While they both work fine when used separately, when I use them together, like with
Venue::withNameOrCategory('myname')
->withDistanceFrom(35.0386, 13.92)
->take(5)->get();
I get this error:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'name' in where clause is ambiguous (SQL: select `venues`.*, 6378.1 * ACOS(COS(RADIANS(35.0386)) * COS(RADIANS(geo_latitude)) * COS(RADIANS(13.92) - RADIANS(geo_longitude)) + SIN(RADIANS(35.0386)) * SIN(RADIANS(geo_latitude))) AS distance, (SELECT (distance - (distance / 100 * distance_bonus))) as distance_bonused from `venues` left join `venue_plans` on `venues`.`id` = `venue_plans`.`venue_id` where (`name` like %myname% or exists (select * from `categories` inner join `category_venue` on `categories`.`id` = `category_venue`.`category_id` where `venues`.`id` = `category_venue`.`venue_id` and `categories`.`name` like %myname%)) order by `distance_bonused` desc, `distance` desc limit 5)
I should mention that even the venue_plans table has a name column, to store the selected plan name and keep it specific for that venue.
Can somebody explain to me what's the cleanest method to do this? I would also avoid to use table names and joins if possible.
Thanks

Related

PHP/PDO Select from columns multiple conditions

I am trying to obtain results for a given member where status is pending or accepted doing the below:
$status1 = "Pending";
$status2 = "Attended";
$query = $conn->prepare('SELECT * FROM members WHERE member_id=:mID AND status=:status1 OR status=:status2');
$query->execute(array(':mID' => $mID,':status1' => $status1, ':status2' => $status2));
if ($query->rowCount() > 0) {
//start to create my table
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
//create variable, loop through and fill the table etc
}
}else{
echo "something";
}
This displays data - however, it even obtains results not specific to the member id (mID). Meaning other members data too!
I'm clearly missing something and or my query is wrong but struggling to find anything..
Any help appreciated.
You need to look at operator precedence for your database. You're doing this:
SELECT * FROM members WHERE member_id = :mID AND status = :status1 OR status = :status2;
Which most likely results in this:
SELECT * FROM members WHERE (member_id = :mID AND status = :status1) OR status = :status2;
But that's not what you want, so you will have to explicitly use parens like this:
SELECT * FROM members WHERE member_id = :mID AND (status = :status1 OR status = :status2);
Alternatively, you can use IN so that there's no OR:
SELECT * FROM members WHERE member_id = :mID AND status IN (:status1, :status2);

Implementing a join in doctrine query builder not working correctly

I am trying to get information from one table based on information in another table, which is linked by an ID.
The two tables are: property and unit.
I need to gather all the units within a property but ONLY if the property has a status of '1' and a hidden flag of '0'. In normal mySQL, I wrote:
SELECT u.* FROM unit u INNER JOIN property p ON p.id = u.property WHERE p.status = 1 AND p.hidden = 0
which produces the correct results, although when I try the same using querybuilder:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('u')
->from('AppBundle:Unit', 'u')
->join('u', 'AppBundle:Property', 'p', 'u.property = p.id')
->where('p.status = :status')
->andWhere('p.hidden = :hidden')
->setParameter('status', 1)
->setParameter('hidden', 0);
return $qb->getQuery()->getResult();
Using information I gleaned from the Doctrine Query Builder documentation. However, when I load the page I get the following error:
[Semantical Error] line 0, col 42 near 'u AppBundle:Property': Error:
Class 'u' is not defined.
The query being executed:
SELECT u FROM AppBundle:Unit u INNER JOIN u AppBundle:Property P u.property = p.id WHERE p.status = :status AND p.hidden = :hidden
Can anyone help figure out what I'm doing wrong in my query?
try to change this:
->join('u', 'AppBundle:Property', 'p', 'u.property = p.id')
to this:
->join('AppBundle:Property', 'p', 'WITH', 'u.property = p.id')
You should exchange your first and second arguments places, because join() method is:
/**
* Creates and adds a join over an entity association to the query.
*
* The entities in the joined association will be fetched as part of the query
* result if the alias used for the joined association is placed in the select
* expressions.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u')
* ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
* </code>
*
* #param string $join The relationship to join.
* #param string $alias The alias of the join.
* #param string|null $conditionType The condition type constant. Either ON or WITH.
* #param string|null $condition The condition for the join.
* #param string|null $indexBy The index for the join.
*
* #return QueryBuilder This QueryBuilder instance.
*/
public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
This is a doc from doctrine QueryBuilder class.

Eloquent: Nested query to revert order in limited result

The following function (in my User Model) gives me the correct result for my chat system. Almost... I need to revert the order of the results.
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take($limit)->get();
return $res;
}
It returns an object and I need an object as result. I tried already to convert the result to an array, revert the order and then convert the array back to object. This didn't work.
What I need is a nested query like the following raw SQL query:
SELECT *
FROM (
SELECT *
FROM chatmessages
WHERE (
receipient = '422'
AND sender = '22'
)
OR (
receipient = '22'
AND sender = '422'
)
ORDER BY created_at DESC
LIMIT 0 , 20
)faketable
ORDER BY created_at ASC
There are a few articles with nested queries, but I don't find a similar case and it would be good if someone could do this in Eloquent without the use of Raw queries... It must be possible.
Try this..
use take() and skip(),offset()
get 4 items from offset 3/4th:
Chatmessage::take(4)->offset(3)->get();
Or this (get 10 items from 8rd row):
Chatmessage::take(10)->skip(2)->get();
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take(3)->skip(2)->get();
return $res;
}

Mapping location in MySQL

I have two tables:
1) One is the location table that is kept from android phone consists of username, latitude, longitude, date, time.
2) Another one is table that I kept country, region, province, postal code, city, latitude, longitude.
I want to mapping location(lat,lng) of table 1) using table 2) before insert to db.
It's look simple but the problem is location of table 2) is just a stable point, otherwise the location of table 1) are points which traversal of each city.
So, the location of table 1) is not similar to location of table 2).
Any one have idea for this problem ? Any formula or technique ?
Appreciate your help.
edit: I tried this statement before insert statement
$city = mysql_query("SELECT p.city
FROM place AS p
ORDER BY ACOS(SIN(p.lng)*SIN('".$lng."')+COS(p.lng)*COS('".$lng."')*COS(p.lat-'".$lat."'))",$con);
but the result is Resource id #3 in the field, other fields also shown like this.
c
so somthing like:
SELECT regions.*
FROM users, regions
WHERE users.user_id = $user_id
ORDER BY
ACOS (
SIN(users.long) * SIN(regions.long) +
COS(users.long) * COS(regions.long) * COS(regions.lat - users.lat)
)
LIMIT 1
Added 2011-08-15
or in php like your exemple
$query = "SELECT city
FROM place
ORDER BY
ACOS(
SIN(lng) * SIN({$lng}) +
COS(lng) * COS({$lng}) * COS(lat - {$lat})
)";
$resource = mysql_query($query);
$result = mysql_fetch_assoc($resource);
$city = $result['city'];
Thank you for every help.
$qc = mysql_query("SELECT *
FROM place AS p
ORDER BY MIN(ACOS(SIN(p.lng)*SIN({$lng})+COS(p.lng)*COS({$lng})*COS(p.lat-{$lat})))", $con);
while ($row = mysql_fetch_assoc($qc)) {
$city = $row['city'];
$district = $row['district'];
$province = $row['province'];
$region = $row['region'];
$country = $row['country'];
mysql_query("INSERT INTO " . $username . "_logs_" . $type . "(username,date,
time,lat,lng,city,district,province,region,country,note,color)
VALUES('".$username."','".$date."','".$time."',
'".$lat."','".$lng."','".$city."','".$district."'
,'".$province."','".$region."','".$country."','unknown','unknown')", $con)
or die("Cannot Insert to Table");
mysql_close();
}
This is my final answer. I want to share for whoever weak in query like me :))

how can I connect these two tables in sql?

i need to take only the items from the table "__sobi2_item" that are in the same country of the user.And use this results for the rest of the function Showupdatelisting. This is my php script:
<?php
function showUpdatedListing()
{
//i found the user country field value...
global $database;
$user =& JFactory::getUser();
$userId = $user->get( 'id' );
$sql = "SELECT (id) FROM #__community_fields WHERE fieldcode= 'FIELD_COUNTRY'";
$database->setQuery( $sql );
$fieldID = $database->loadResult();
$sql = "SELECT (value) FROM #__community_fields_values WHERE field_id= {$fieldID} && user_id= {$userId}";
$database->setQuery( $sql );
$usercountry = $database->loadResult();
// From all the entries i take only ones that have country field like the user has...
$query = "SELECT `data_txt`, `itemid`, `fieldid` FROM `#__sobi2_fields_data` WHERE (`fieldid` = 6) AND ('data_txt' = {$usercountry})";
$database->setQuery($query);
$ResultsArray = $database->loadObjectList();
// We need something here like a Query to load only the entries from $ResultsArray... ??
//....instead of this...
$config =& sobi2Config::getInstance();
$database = $config->getDb();
$now = $config->getTimeAndDate();
$query = "SELECT itemid FROM #__sobi2_item WHERE (published = 1 AND publish_down > '{$now}' OR publish_down = '{$config->nullDate}') ORDER BY last_update DESC LIMIT 0, 30";
$database->setQuery($query);
$sids = $database->loadResultArray();
// ......... show update function goes on...
?>
can anyone help me to connect and adjust these query? thanks.
NB:with the last query (4) i need to filter items of the $ResultsArray taking only ones published and ordering them by last_update. i know it is wrong and now there is no connection with the query before. This is how i have tables in mysql:
_sobi2_fields_data:
itemid
fieldid
data_txt --->(is a description column for each field)
_sobi2_item:
itemid
published --->( 1 if true, 0 if false )
last_update --->(date of the last update for the item, also equal to the publication date if there are no changes)
thanks.
I don't know what you are trying to ask as well. Your last query (number 4) doesn't make sense to me, how is it linked to the above queries?
[EDIT] I've linked your 4th table above assuming itemid is the primary key for the items in sobi2_item table and that the value is linked to the sobi_fields_data table via itemid.
SELECT
cf.id,
sfd.data_txt,
sfd.itemid,
sfd.fieldid
FROM #__community_fields cf
INNER JOIN #__community_fields_values cfv ON cf.id=cfv.field_id
INNER JOIN #__sobi2_fields_data sfd ON cfv.value=sfd.data_txt
INNER JOIN #__sobi2_item si ON sfd.itemid=si.itemid
WHERE cf.fieldcode='FIELD_COUNTRY'
AND cfv.user_id=$userId
AND sfd.fieldid=6
AND si.published=1
AND (si.publish_down > '{$now}' OR si.publish_down = '{$config->nullDate}')
ORDER BY si.last_update DESC LIMIT 0, 30
Good luck!