Problem:
I want to run this query in cakephp 3
SELECT
medicine_records.*,
medicines.name AS medicine_name,
medicines.subname AS `medicine_subname`
FROM medicine_records
INNER JOIN medicines ON medicines.id = medicine_records.medicine_id
Controller:
$medicines = $this->MedicineRecords->find()->contain(['Medicines'])->all();
Cakephp Queries log:
2017-02-14 09:58:47 Debug: duration=1 rows=1 SELECT MedicineRecords.id AS `MedicineRecords__id`, MedicineRecords.user_id AS `MedicineRecords__user_id`, MedicineRecords.medicine_id AS `MedicineRecords__medicine_id`, MedicineRecords.times_pre_day AS `MedicineRecords__times_pre_day`, MedicineRecords.created AS `MedicineRecords__created`, MedicineRecords.modified AS `MedicineRecords__modified`, Medicines.id AS `Medicines__id`, Medicines.name AS `Medicines__name`, Medicines.subname AS `Medicines__subname`, Medicines.type AS `Medicines__type`, Medicines.created AS `Medicines__created`, Medicines.modified AS `Medicines__modified` FROM medicine_records MedicineRecords INNER JOIN medicines Medicines ON Medicines.id = (MedicineRecords.medicine_id)
Result data:
"data": [
{
"id": 1,
"user_id": 1,
"medicine_id": 3,
"times_pre_day": 2,
"created": "2017-02-14T04:55:48+00:00",
"modified": "2017-02-14T04:55:48+00:00",
"medicine": {
"id": 3,
"name": "Name",
"subname": "Subname",
"type": 1,
"created": "2017-02-13T09:38:48+00:00",
"modified": "2017-02-13T09:38:48+00:00"
}
}
]
This result has medicine in child level. I want all data in top level like this:
"data": [
{
"id": 1,
"user_id": 1,
"medicine_id": 3,
"times_pre_day": 2,
"medicine_name": "Name",
"medicine_subname": "Subname",
"created": "2017-02-14T04:55:48+00:00",
"modified": "2017-02-14T04:55:48+00:00",
}
]
I already try to use public $recursive = -1; but I think it not working on Cakephp 3.
Update
Remove WHERE medicine_records.id = 1
Obviously that is the expected result,contain come in nested form and it should come this way.
No worries you can select fields within contain as you need:
$medicines = $this->MedicineRecords->find()
->contain(['Medicines' => function ($q) {
return $q
->select(['medicine_name'=>'name','medicine_subname'=>'subname']);
}])->all();
See Here (select within contain CakePHP 3).
Related
I am in a situation where I want to get the form for each team of based on competition
Team model
public function standing()
{
return $this->hasMany(Standing::class);;
}
Competition model
public function standing()
{
return $this->hasMany(Standing::class);;
}
Each Team has many Standing
Each Competition has many Standing
standing table
team_id
competition_id
form
my code
$f = Fixture::where('status', 'NS')
->with('teamA', 'teamB', 'competition')
->get();
since all fixtures of all competitions are coming, but once team can be in two competition,
hence its form is different.
[
{
"name": "Leicester vs Newcastle",
"team_a": {
"id": 10,
"name": "Leicester",
**"form": null**
},
"team_b": {
"id": 2,
"name": "Newcastle",
**"form": null**
},
"competition_id": 1,
"competition": {
"id": 1,
"name": "Premier League",
},
},
{
"name": "Leeds vs Tottenham",
"id": 344,
"team_a": {
"id": 19,
"name": "Leeds",
"form": null
},
"team_b": {
"id": 11,
"form": null
},
"competition_id": 2,
"competition": {
"id": 1,
"name": "Other League",
},
},
]
I have tried to explain the situation,
can someone please tell me how to get this?
for ex.
in champions league(competition), man. united has form WWWLDW, but same team in another league might be different
I'm trying to use mongodb to do a query link SQL join table in PHP
Two collection are like following
Courses:
{
"CourseID": "CS101",
"Title": "Introduction to Data Science",
"Level": 6
},
{
"CourseID": "CS102",
"Title": "Application Design and Development",
"Level": 6
},
Offer:
{
"DeptID": "CS",
"CourseID": "CS101",
"Year": 2016,
"ClassSize": 40,
"AvailablePlaces": 40
},
I'm trying to make the result like the following:
prefering result here
MongoDB is not a relational database, but you can perform a left outer join by using the $lookup stage.
The $lookup stage lets you specify which collection you want to join with the current collection, and which fields that should match. Here's the official doc.
dbo.collection('courses').aggregate([
{ $lookup:
{
from: 'offer',
localField: 'CourseID',
foreignField: 'CourseID',
as: 'availableplaces'
}
}
])
I wonder if anyone can help, as I've hit a wall and still learning Laravel ORM. Can anyone explain why, when I run:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id');
}
I get this result set: (snipped...)
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": [
{
"user_id": 1,
"location_tag_id": 1,
"id": 1,
"created_at": "2015-05-13 13:06:28",
"updated_at": "2015-05-13 13:06:28",
"name": "Test Tag 0",
"location_id": 1,
"deleted_at": null
},
{
"user_id": 1,
"location_tag_id": 2,
"id": 2,
"created_at": "2015-05-13 11:40:21",
"updated_at": "2015-05-13 12:56:13",
"name": "Test Tag 123",
"location_id": 1,
"deleted_at": null
}
]
}
Which is ace! However, when I start to select the columns I want from the location_tags join, with:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id')
->select('lt.id', 'lt.name');
}
I end up with:
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": []
}
Can someone explain what's going on? And possibly point me in the right direction to limit the selects? Thanks!
Update
I've also tried:
$query = \App\User::with(['permissions', 'locationTags' => function($query){
$query->select('lt.id', 'lt.name');
}]);
Which returns the same result :(
Figured it out. The key here was that you must include a select() value of at least one key that Laravel can use to map the result set. In my case it was user_id, like so:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id')
->select('user_id', 'lt.name', 'location_tag_id');
}
Which then returns a much nicer results set:
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": [
{
"user_id": 1,
"name": "Test Tag 0",
"location_tag_id": 1
},
{
"user_id": 1,
"name": "Test Tag 123",
"location_tag_id": 2
}
]
}
Hope this helps someone out in the future, because it kept me guessing for a good couple of hours.
I'm sorry but you've gone a little sideways here. The relationship definition should just define the Relationship. It's a powerful feature that supports many other aspects of the ORM. What you've done here is more or less build a custom Query, severely limiting the effectiveness of the relationship.
Up until recently, the proper way would have looked something like this.
// class User
public function userHasLocationTags() {
$this->hasMany('App\UserHasLocationTags', 'user_id');
}
// class UserHasLocationTags
public function locationTags() {
$this->hasMany('App\LocationTags', 'location_tag_id');
}
And you would eager load all the results like this.
$user = User::where('id', 1)->with('userHasLocationTags.locationTags')->first();
The code above produces in 3 queries. One to get the User, one to get all the UserHasLocationTags and a one to get all the LocationTags. This may seem wasteful on first blush but consider the following.
$users = User::take(100)->with('userHasLocationTags.locationTags')->get();
Again, this is only 3 queries but now you have loaded 100 users with all their location tags.
But I can see you are a man with an eye for efficiency, and loading all the intermediate relationships and the whole nested hierarchy probably doesn't sit well with you. Well good news! Laravel 5 has added another relationship type for just this situation. hasManyThrough (scroll down a bit to find it).
Has Many Through
The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation. For example, a Country model might have many Post through a User model.
So in your case it may look something like this...
// class User
public function locationTags()
{
return $this->hasManyThrough('App\LocationTags', 'App\UserHasLocationTags');
}
$users = User::take(100)->with('locationTags')->get();
So now we are down to two queries. If you feel the need to optimize this further and only select specific columns, you can modify the eager load.
$users = User::take(100)->with(['locationTags' => function ($query)
{
$query->select('user_id', 'name', 'location_tag_id');
})->get();
And if you do that frequently, you should wrap it up in a scope.
I'm trying to group data in a table by cat_id and convert the result into JSON.
Table structure
id name cat_id
1 A 2
2 B 1
3 C 2
4 D 2
5 E 3
6 F 2
7 G 1
Here is the code I tried
$posts = Posts::groupBy('cat_id')->get();
return $posts->toJson();
Result should look like this:
[
{
"id": "1", //cat_id
"posts": [
{
"id": "2", //user id
"s": "B" //name
},
{
"id": "7",
"s": "G"
}
]
},
{
"id": "2", //cat id
"posts": [
{
"id": "1",
"name": "A"
},
{
"id": "3",
"name": "C"
},
{
"id": "4",
"s": "D"
},
{
"id": "6",
"s": "F"
}
]
}
]
But I'm getting Call to a member function toJson() on a non-object. Where am I doing wrong?
Unfortunately groupBy doesn't work like this. It's more like SQLs GROUP BY (actually that's what it does in the background...)
I assume your error comes from the fact that it doesn't know what to do with name and id because you always need aggregate fields with GROUP BY.
Now here's how you can achieve your goal. I assume you have set up the relation between Category and Post. somewhat like this:
class Category extends Eloquent {
public function posts(){
return $this->hasMany('Post', 'cat_id');
}
}
Then you are able to do:
$categories = Category::with('posts')->get();
And you get all the categories containing its posts. toJson() should work without a problem as well.
#lukasgeiter suggested right thing to do, however there's another way to go too. Depending on your needs you can choose the one for you:
$posts = Pots::all(); // fetch from the DB
$groupedByCat = $posts->groupBy('cat_id'); // call group by on the collection
Result will look like this:
{
"1": // cat_id
[
{
"id": "5" // post data
...
},
...
],
"2":
...
}
I need to update my code to include Media, which is oneToMany unidirectional mapped in Object.php Entity. Ideally I need my code to return all objects even though without media, currently it only returns objects WITH media but it returns multiple results if there are multiple media items.
If you think I refactor the code and use something much simpler, than let me know - I didn't find a way to get the feed using the getRepo() and findBy() functions (no serialization?)
Here's my code:
// Build object QB
$emObject = $this->objectService->getManager();
$qbObject = $emObject->createQueryBuilder();
$qbObject->addSelect(['o.id', 'o.title', 'o.description', 'm as multimedia', 'c.id as category_id', 'c.name as category_name'])
->from('Application\Entity\Cms\Object', 'o')
->leftJoin('Application\Entity\Cms\Media', 'm', 'WITH', '1 = 1')
->leftJoin('o.media', 'om')
->leftJoin('Application\Entity\Cms\Category', 'c', 'WITH', '1 = 1')
->leftJoin('o.category', 'oc')
->andWhere('om.id = m.id')
->andWhere('oc.id = c.id')
->andWhere('o.member = :member')
->setParameter('member', $member);
$objectList = $qbObject->getQuery()->getResult(Query::HYDRATE_ARRAY);
$viewModel->setVariables(['object' => $objectList]);
return $viewModel;
Which returns:
{
"object": [
{
"multimedia": {
"id": 22,
"title": null,
"url": "\/srv\/pois\/cms\/e9049aaac93ce96c81e16340027a804b.png"
},
"id": 34,
"title": "imgTest",
"description": "f",
"category_id": 1,
"category_name": "Chambres"
},
{
"multimedia": {
"id": 23,
"title": null,
"url": "\/srv\/pois\/cms\/ce0c7d669c567369485a01c65197943f.png"
},
"id": 34,
"title": "imgTest",
"description": "f",
"category_id": 1,
"category_name": "Chambres"
}
]
}
However, I need the multimedia objects to be in one array and only return 1 object, like:
{
"object": [
{
"multimedia": {
1: {
"id": 22,
"title": null,
"url": "\/srv\/pois\/cms\/e9049aaac93ce96c81e16340027a804b.png"
},
0: {
"id": 23,
"title": null,
"url": "\/srv\/pois\/cms\/ce0c7d669c567369485a01c65197943f.png"
},
}
"id": 34,
"title": "imgTest",
"description": "f",
"category_id": 1,
"category_name": "Chambres"
}
]
}
I always work with DQL queries without QueryBuilder. I think this is what you want:
$query = $this->entityManager->createQuery(
"SELECT o, m FROM 'Application\Entity\Cms\Object' o JOIN o.multimedia m WHERE o.member = :member"
);
$query->setParameters(array('member' => $member));
$objects = $query->getResult();
I don't think it is more complicated than that.
In QueryBuilder it probably looks like this:
$qb = $this->entityManager->createQueryBuilder();
$qb->select('o', 'm')
->from('Application\Entity\Cms\Object', 'o')
->leftJoin('o.multimedia', 'm')
->where('o.member = :member')
->setParameter('member', $member);
return $qb->getQuery()->getResult();
Please come back with details if this doesn't return what you want. I might be able to help out to get what you want.