I'm absolutely new to php/laravel world so sorry if my question is simple.
Language: php with laravel.
What : I want to get all objects who contain the name of the users.
//Exemple of my users Array
($users = ["name1", "name2","name4"])
//Forms is an array who contain multiple object, each object have a reference to a user Name.
$Forms = [{_id: 1, title : "title1", userName : "name1" }, {_id: 2, title : "title2", userName : "name2" }, {_id: 3, title : "title3", userName : "name3" }, {_id: 4, title : "title4", userName : "name4" },{_id: 5, title : "title5", userName : "name1" }]
//here i want to get form with name1, name2 and name4
foreach ($users as $user) {
$allForm = Forms::where('userName ', $user)->get();
};
Problematic: I only received 2 objects (objects from the first user of my array).
Exemple: here i want to get every forms who contain "name1", "name2","name4" but i will received only every forms with "name1".
This should work as per your requirements:
$users = ["name1", "name2","name4"];
$Forms = [
[
'id' => 1,
'title' => 'title1',
'userName' => 'name1'
]
];
$collection = collect($Forms);
$results = $collection->whereIn('userName', $users)->toArray();
dd($results);
use toArray() function to convert the collection
Try this
$users = ["name1", "name2","name4"];
$forms = Form::whereIn('userName', $users)->get();
Related
I'm trying to build an aggregation query in Parse's PHP SDK, and I'm stuck in the "lookup" area, I saw a JS example regarding this but it doesn't work in my case.
I have a table of users, which contains a "Tags" field of type Array, the array is actually an array of pointers, that point to a separate Tag class.
What I'm trying to achieve is to list most popular Tags based on their usage, so basically I need to query the users class and group the Tags that exist in the array, I already achieved this, but I'm stuck with the lookup part, the query currently returns an array of Tags pointers, what I want is to pull the object of those pointers.
Here's what I have currently:
$query = new ParseQuery('_User');
$pipeline = [
'project' => ['tags' => 1],
'unwind' => '$tags',
'group' => [
'objectId' => '$tags.objectId',
'count' => ['$sum' => 1]
],
'sort' => [ 'count' => -1],
'limit' => 10,
];
try {
return $query->aggregate($pipeline);
} catch (ParseException $ex) {
return $ex->getMessage();
}
And here's a snippet of what the _User collection looks like:
{
"_id" : "5BuBVo2GD0",
"email" : "test#test.com",
"username" : "test#test.com",
"lastname" : "Doe",
"firstname" : "John",
"_created_at" : ISODate("2017-01-23T09:20:11.483+0000"),
"_updated_at" : ISODate("2019-02-15T02:48:30.684+0000"),
"tags" : [
{
"__type" : "Pointer",
"className" : "Tag",
"objectId" : "St2gzaFnTr"
},
{
"__type" : "Pointer",
"className" : "Tag",
"objectId" : "LSVxAy2o74"
}
],
"_p_country" : "Country$4SE8J4HRBi",
}
And the Tag collection looks like this:
{
"_id" : "St2gzaFnTr",
"name" : "Music",
"_created_at" : ISODate("2018-10-22T20:00:10.481+0000"),
"_updated_at" : ISODate("2018-10-22T20:00:10.481+0000")
}
Any help would be appreciated!
Thanks in advance
Not sure if this is a direct answer, but here's a working aggregation on tags sorting for freq...
public function tagHistogram(Request $request, Response $response, array $args): Response {
$pipeline = [
'unwind' => '$tags' ,
'sortByCount' => '$tags',
'limit' => 1000,
];
$query = new ParseQuery('Product');
$result = $query->aggregate($pipeline);
$result = array_map(
function ($e) {
$e['name'] = $e['objectId'];
unset($e['objectId']);
return $e;
},
$result
);
return $response->withJson($result);
}
I am at my first steps with mongoDB and php, trying to figure out how aggregations works. I have an approximate idea on how to use them from the command line but I am trying to translate this for the php driver. I am using the restaurants dexample DB, a list of records like this
{
"_id" : ObjectId("59a5211e107765480896f3f8"),
"address" : {
"building" : "284",
"coord" : [
-73.9829239,
40.6580753
],
"street" : "Prospect Park West",
"zipcode" : "11215"
},
"borough" : "Brooklyn",
"cuisine" : "American",
"grades" : [
{
"date" : ISODate("2014-11-19T00:00:00Z"),
"grade" : "A",
"score" : 11
},
{
"date" : ISODate("2013-11-14T00:00:00Z"),
"grade" : "A",
"score" : 2
},
{
"date" : ISODate("2012-12-05T00:00:00Z"),
"grade" : "A",
"score" : 13
},
{
"date" : ISODate("2012-05-17T00:00:00Z"),
"grade" : "A",
"score" : 11
}
],
"name" : "The Movable Feast",
"restaurant_id" : "40361606"
}
I just want to count how many restaurants for location, what I am doing is
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->myNewDb->restaurants;
$results = $collection->aggregate(
[
'name' => '$name'
],
[
'$group' => [
'cuisine' => ['sum' => '$sum']
]
]
);
and I am getting this error
Fatal error: Uncaught exception 'MongoDB\Exception\InvalidArgumentException'
with message '$pipeline is not a list (unexpected index: "name")'
any idea? I can't find any good documentation on php.net.
thanks
M
Just take a look into documentation, and you will see, that the pipelines must be passed as an array.
The aggregate method accepts two parameters $pipelines and $options (public function aggregate(array $pipeline, array $options = [])).
Also as was mentioned before, the $group must have the _id element.
Groups documents by some specified expression and outputs to the next
stage a document for each distinct grouping. The output documents
contain an _id field which contains the distinct group by key. The
output documents can also contain computed fields that hold the values
of some accumulator expression grouped by the $groupās _id field.
$group does not order its output documents.
https://docs.mongodb.com/manual/reference/operator/aggregation/group/
So your code must look like this:
$results = $collection->aggregate([
[
'$group' => [
'_id' => '$cuisine',
'sum' => ['$sum' => 1],
'names' => ['$push' => '$name']
]
]
]);
This code groups documents by cuisine element, counts the items and collects all name values into array.
I have the following json file with products details:
"products": [
{
"sku": 123,
"name": "iphone 7",
"categoryPath": [
{
"id": "abcat0800000",
"name": "Cell Phones"
},
{
"id": "pcmcat209400050001",
"name": "All Cell Phones with Plans"
}
],
}
]
I would like only to store the last value (ID and NAME) of the categoryPath Array:
"id": "pcmcat209400050001",
"name": "All Cell Phones with Plans"
My current code takes the json file, decode the json and insert in products table the information.
$json = File::get("/json/cell-0.json");
$data = json_decode($json);
$array1 = (array)$data;
//table products
foreach ($array1['products'] as $obj) {
DB::table('products')->insert(array(
'productSku' => ((isset($obj->sku) ? $obj->sku : 1)),
'productName' => ((isset($obj->name) ? $obj->name : null)),
'categoryId' => end($obj->categoryPath->id),
'categoryName' => end($obj->categoryPath->name)
));
Taking into consideration that array->categoryPath have multiple fields I would like to use a function (eg: end()) in order to take id and name only of the last values.
Using end($obj->categoryPath->id) I receive the following error ->
Attempt to modify property of non-object
Is this the best way to retrieve the last value of a multidimensional array?
You could use end() probably but your accessors would have to be outside the end() call (untested):
foreach ($array1['products'] as $obj) {
DB::table('products')->insert(array(
'productSku' => ((isset($obj->sku) ? $obj->sku : 1)),
'productName' => ((isset($obj->name) ? $obj->name : null)),
'categoryId' => end($obj->categoryPath)->id,
'categoryName' => end($obj->categoryPath)->name
));
The way you're getting the last element is incorrect, here is the refactored code. I also eliminated the need to cast data as an array as well.
$json = File::get("/json/cell-0.json");
$data = json_decode($json, true);
//table products
foreach ($data['products'] as $product) {
$lastCategory = isset($product['categoryPath']) && $size = sizeof($product['categoryPath']) ? $product['categoryPath'][$size-1] : array('id' => null, 'name' => null);
DB::table('products')->insert(
array(
'productSku' => isset($product['sku']) ? $product['sku'] : 1,
'productName' => isset($product['name']) ? $product['name'] : null,
'categoryId' => lastCategory['id'],
'categoryName' => lastCategory['name']
)
);
}
For example, we have a songs table, and a favorites (songs) table.
If I use Songs::with('favorites')->get(), it will return as follows:
"songs": [
{
"id": 43,
"name": "Song 1",
"favorites": [
{
"id": 52,
"user_id": 25,
"song_id": 43
}
]
},
{
"id": 44,
"name": "Song 2",
"favorites": []
},
What I want to do is, in case the song has been favorited, return 1, and if not, return 0, as follows:
{
"id": 43,
"name": "Song 1",
"favorites": true (or 1)
},
{
"id": 44,
"name": "Song 2",
"favorites": false (or 0)
},
Is there a way to do it without having to run through the returned collection array manually in PHP?
I think this is the simplest solution using Eloquent
Song::withCount([
'favorites' => function ($query) {
$query->select(DB::raw('IF(count(*) > 0, 1, 0)'));
}
])->orderBy('favorites_count', 'desc');
There are a bunch of different ways to do what you want, it all depends on what works best for you.
If you want the result returned in your result set, you can setup a scope:
class Song extends Model
{
public function scopeAddFavorite($query, $userId = null)
{
$andUser = !empty($userId) ? ' AND favorites.user_id = '.$userId : '';
return $query->addSelect(\DB::raw('(EXISTS (SELECT * FROM favorites WHERE favorites.song_id = songs.id'.$andUser.')) as is_favorite'));
}
}
Since this scope modifies the 'select' statement, you need to make sure to manually specify the other columns you want before adding in the scope.
$songs = Song::select('*')->addFavorite()->get();
I added in the ability to pass in the user id, in case you wanted to specify that the column only return true if the song has been favorited by a specific user.
$songs = Song::select('*')->addFavorite(25)->get();
Another option, you can add an accessor to your model that will handle the check for you. You can read about accessors here.
class Song extends Model
{
// only do this if you want to include is_favorite in your json output by default
protected $appends = ['is_favorite'];
public function getIsFavoriteAttribute()
{
// if you always want to hit the database:
return $this->favorites()->count() > 0;
// if you're okay using any pre-loaded relationship
// will load the relationship if it doesn't exist
return $this->favorites->count() > 0;
}
}
Usage:
$song = Song::find(1);
// access is_favorite like a normal attribute
var_dump($song->is_favorite);
// with $appends, will show is_favorite;
// without $appends, will not show is_favorite
var_dump($song);
Using a raw SQL approach
DB::select('SELECT s.*, (CASE WHEN f.id IS NULL THEN 0 ELSE 1 END) as favorites FROM songs s LEFT JOIN favorites f ON s.id = f.song_id GROUP BY s.id')
will return the following structure:
[
StdClass { 'id' => 1, 'name' => 'Song 1', 'favorites' => 1 },
StdClass { 'id' => 2, 'name' => 'Song 2', 'favorites' => 0 },
StdClass { 'id' => 3, 'name' => 'Song 3', 'favorites' => 1 },
]
Now, using Eloquent
Song::withCount('favorites')->get()
will return the an array of objects of the Song class
[
Song { 'id' => 1, 'name' => 'Song 1', 'favorites_count' => 1 },
Song { 'id' => 2, 'name' => 'Song 2', 'favorites_count' => 0 },
Song { 'id' => 3, 'name' => 'Song 3', 'favorites_count' => 3 }
]
The difference is the first will return an array of PHP standard objects while the second an array of Song objects and the first is faster than second.
You can't do something like this without somehow manipulating the original results object first.
You don't really specify how you need to use or iterate through the data, but maybe Query Scopes can help you? Either way you'll need to iterate over the data once to manipulate it. A high order function like map will help you do this.
suppose you have the $songs array, you can do this,
foreach($songs as $song)
$song["favorites"] = is_null($song["favorites"]) ? true : false;
In mongoDB i have two collection users and posts following this structure:
Posts
{
_id: ObjectId(""),
subject: "some post",
content: "here is the content",
user_id: "4351"
}
Users
{
user_id: "4351",
name: "John Marks",
picURL: "http://...",
aboutme: "historian of the future"
}
needing to get the posts in array with name.
db.posts.find().map(function(newPost){
newPost.name = db.users.findOne({user_id: newPost.user_id}).name;
return (newPost);
})
I wrote this code and it's work in mongoshell well returning this result:
{
_id: ObjectId(""),
subject: "some post",
content: "here is the content",
user_id: "4351",
name: "John Marks"
}
but i could not apply in php. You can't just simple get the output of the map function. It requires reduce function and output collection for the returning value.
Edit:
$map = new MongoCode('
function(newPost) {
newPost.username = db.users.findOne({user_id: newPost.user_id}).name;
return newPost;
}
');
post = $app->mongo->command(array(
"mapreduce" => "posts",
"map" => $map,
"reduce" => '',
"out" => array("inline" => 1)
));
var_dump($post);
This code must be work but accessing another collection in map function via 'db' is forbidden after mongo 2.4 release. That's why i changed my approach. Instead of using map/reduce, handled with php. Added posts user_ids to array and get the users information with following code.
$userInf = $app->mongo->selectCollection("users")->find(
array('user_id' => array('$in' => $user_ids)),
array("_id" => 0, "user_id" => 1, "name" => 1, "picURL" => 1)
);