I guys I am working diwth doctrine JOINS and I came across a problem.
I have done a query with joins and it seems ok, the results are good! but the output is terrible
public function getMarketcapData($page = -1, $limit = -1){
$pageAndCount = $page>-1 && $limit>0;
$qb = $this->getEntityManager()->createQueryBuilder();
$q = $qb->select('cc, count(cr) as coinRawsCount, cmc as relatedCategory')
->from('AppBundle:CoinClean', 'cc')
->leftJoin("AppBundle:CoinRaw", "cr", 'WITH', 'cc = cr.coinClean')
->leftJoin("AppBundle:CoinMapCategory", "cmc", 'WITH', 'cc.relatedCategory = cmc')
->andWhere('cc = cr.coinClean')
->andWhere('cc.relatedCategory = cmc')
->groupBy('cc.id')
;
if($pageAndCount) $q = $q->setFirstResult($page*$limit)->setMaxResults($limit);
$q= $q->orderBy('cc.rank', "ASC")->getQuery();
$result = $q->getResult();
if($pageAndCount){
$qb = $this->getEntityManager()->createQueryBuilder();
$q = $qb->select('count(u.id)')->from('AppBundle:CoinClean', 'u')->getQuery();
return new PageResult($result, (int)$q->getSingleScalarResult(), $page, $limit );
}else{
return $result;
}
}
here you have the output: it is an array made by array + obejcts
data:[
[ // first query result
{data from cc} // ony one object
],
{// first query result from join
"coinRawsCount": 4,
"relatedCategory": {...}
},
[ // second query result
{data from cc2} // ony one object
],
{// second query result from join
"coinRawsCount": 4,
"relatedCategory": {...}
}
]
my goal is to condense all in
"data":[
{
"data": {...},
"coinRawsCount": 4,
"relatedCategory": {...}
},
{
"data": {...},
"coinRawsCount": 4,
"relatedCategory": {...}
},
]
Any ideas?
You could loop over the result and merge on every other row, e.g. like this:
$rows = $q->getResult();
$result = []
foreach ($rows as $index => $row) {
if ($row instanceof App\Entity\CoinClean) {
// Skip first row and merge data when reading next row
continue;
}
$result[] = array_merge(
['data' => $rows[$index - 1]],
$row
);
}
return $result;
Assuming the first row is always the entity, we will skip this entry using continue;. The next row should contain an array with the additional data. We can then take the previous offset ($index - 1) to fetch the entry and the current row and merge them together. You might have to do additional checks.
Keep in mind that you will loop over a potentially large array and since you copy the data over to a new array this might use up a considerable amount of memory. So you might have to profile and optimize it, if you notice poor performance.
Other options would be to perform a native SQL query and then just map the output using a ResultSetMappingBuilder or to use custom hydration, i.e. create a new custom DTO-object for that query, that better fits the resulting data than the original entity structure, using DQL SELECT NEW App\Dto\MyModel FROM ...your query...'.
Related
i am new to Laravel and i am trying to do the following:
i have 2 Collections: "requirements" and "skills":
$req = collect([
'Installation',
'Konfiguration',
'Automatisierung'
]);
$skills = collect([
'Engineering Team',
'Installation',
'Konfiguration',
'Konfiguration',
'Automatisierung',
'Security',
'Automatisierung',
'Automatisierung',
'Automatisierung'
]);
I am trying the following: I want to take the first item of $req ("Installation") and count how many times it exists in the $skills collection. Then i need to take the second value of the $req collection and do the same thing. At the end i need to have some output like:
Installation exits 2 times
Konfiguration exists 2 times
Automatisierung exists 3 times
all others -> 0 (or even ignoring it)
i thought about iterating with nested for loops and if clauses and so on like "classic" coding, but isnt there something in laravel to make it nicer? I tried like contains and so on but its like "if" clause.
Thanks a lot in advance
You could use countBy of laravel, it counts depending on a function
Learn more about countBy here
This code would return another array with the 3 sentences you've said
$req->map(function($i) use ($skills) {
$count = $skills->countBy(function ($skill) use ($i) {
return $skill == $i;
});
if (isset($count[1])) {
return $i . " exits ". $count[1] ." times";
} else {
return $i . " doesn't exits";
}
});
You can also just do $skills->countBy() that it would return an collection with key beeing the name of the skill and the value beeing the number of times it appears
[
{
"total": 71
},
{
"total": 66
}
]
How can i sum both numbers up to give me = 137. I have tried array_sum($array_result) but the spits and error saying :
array_sum() expects parameter 1 to be array, object given
this is my code
$result = DB::table('marks')->where([
['term', $request->term],
['subject', $request->subject],
['class', $student->class],
['arm', $student->arm],
])->select('total')->get();
return array_sum($result);
return redirect()->back()->with('success', 'Results marked successfully.');
If you just need to sum up total, use sum() Query Builder method:
$result = DB::table('marks')->where([
['term', $request->term],
['subject', $request->subject],
['class', $student->class],
['arm', $student->arm],
])->sum('total');
Easiest to put them in a collection and call sum() method.
Like that:
$sum = collect($yourArray)->sum('total');
You could do it with foreach and a temporary variable as well though.
$result = DB::table('marks')->where([
['term', $request->term],
['subject', $request->subject],
['class', $student->class],
['arm', $student->arm],
])->select('total')->get();
$result = $result->toArray();
Now you can simply do this as mentioned by devk
collect($result)->sum('total');
get() return collection. you directly perform actions on collections
like get()->sum('total')
I hope this helps.
Sorry, I have a hard time to write proper thread title for this problem.
Here's my question:
In short: How to merge array item and calculate.
Here's the full explanation
1) I have a (WP custom) database to track statistics: visits, unique visits, etc, and it's stored per post per date.
To make it easier to understand. Here's the screenshot of the table:
2) This is the example data when I queried it:
https://gist.github.com/turtlepod/8e7dc93bae7f0b665fd5aea8a9694998
So in this example we have multiple post ID: "90", "121", & "231"
We have multiple date in db: "2017-03-20", "2017-03-21", "2017-03-22"
We have multiple stats: "visits", and "unique_visits"
We also have a "stat_value" for each item.
Each item have unique ID.
All data is dynamically created when an event happen. so not all post_id have 2 stats or the above date.
Note: keep in mind that in real code, we have a lot more data and variations than the example above.
3) I need to merge the data:
The post_id "121" is the same as post "231", so we need to merge and add the "stat_value" into one data and remove "231" entry.
What is the best way to do this (dynamically) via PHP ?
I have this data:
$raw_data = array( ... ); // the one in github gist
$post_groups = array(
'121' => array( '121', '231' ), // main post_id => array of alias.
);
It need to return the same data format as $raw_data, but remove the data of "231" and include/sum the "stat_value" of "231" to "121".
Thank you.
Try it with this:
function david_transform_data($data, $groups) {
if (empty($groups) === true) {
return $data;
}
// Transform groups into a more useful format
$transformed_groups = array();
foreach ($groups as $post_id => $aliases) {
foreach ($aliases as $alias) {
if (absint($post_id) === absint($alias)) {
continue;
}
$transformed_groups[absint($alias)] = $post_id;
}
}
// Replace aliases with the real post id
foreach ($data as $index => $stat) {
if (isset($transformed_groups[absint($stat->post_id)]) === false) {
continue;
}
$data[$index]->post_id = $transformed_groups[absint($stat->post_id)];
}
// Go through stats and merge those with the same post_id, stat_id
// and stat_date
$merged_stats = array();
$index_tracker = 0;
$stats_hash = array();
foreach ($data as $index => $stat) {
$hash_key = sprintf(
'%s-%s-%s',
$stat->post_id,
$stat->stat_id,
$stat->stat_date
);
if (isset($stats_hash[$hash_key]) === true) {
$merged_stats[$stats_hash[$hash_key]]->stat_value += absint($stat->stat_value);
continue;
}
$merged_stats[] = $stat;
$stats_hash[$hash_key] = $index_tracker;
$index_tracker++;
}
return $merged_stats;
}
var_dump(david_transform_data($raw_data, $post_groups));
There might be a faster solution but this is the first thing that came to my mind.
My Mongo collection has two documents
{
"_id":ObjectId("567168393d5c6cd46a00002a"),
"type":"SURVEY",
"description":"YOU HAVE AN UNANSWERED SURVEY.",
"user_to_notification_seen_status":[
{
"user_id":1,
"status":"UNSEEN",
"time_updated":1450272825
},
{
"user_id":2,
"status":"SEEN",
"time_updated":1450273798
},
{
"user_id":3,
"status":"UNSEEN",
"time_updated":1450272825
}
],
"feed_id":1,
"time_created":1450272825,
"time_updated":1450273798
}
Here is the query I used to fetch only if the user_id is 2 & status is "UNSEEN".
**$query = array('$and' => array(array('user_to_notification_seen_status.user_id'=> 2,'user_to_notification_seen_status.status' => "UNSEEN")));**
$cursor = $notification_collection->find($query);
Ideally the above query shouldn't retrieve results but it returning results. If I give an invalid id or invalid status, it is not returning any record.
You're misunderstanding how the query works. It matches your document because user_to_notification_seen_status contains elements with user_id: 2 and status: UNSEEN.
What you can do to get the desired results is use the aggregation framework; unwind the array and then match both conditions. That way you'll only get the unwinded documents with the array element satisfying both conditions.
Run this in mongo shell (or convert to PHP equivalent). Also, change YourCollection to your actual collection name:
db.YourCollection.aggregate([ { $unwind: "$user_to_notification_seen_status" }, { $match: { "user_to_notification_seen_status.status": "UNSEEN", "user_to_notification_seen_status.user_id": 2 } } ] );
This will return no records, but if you change the id to 3 for example, it will return one.
Try:
$query = array(
array('$unwind' => '$user_to_notification_seen_status'),
array(
'$match' => array('user_to_notification_seen_status.status' => 'UNSEEN', 'user_to_notification_seen_status.user_id' => 2),
),
);
$cursor = $notification_collection->aggregate($query);
I am making a real estate related app and I've been having a hard time figuring out how to set up the query so that it would return "Only Apartments or Duplexes within selected areas" I'd like to user to be able to find multiple types of property in multiple selected quadrants of the city.
I have a database with a column "type" which is either "Apartment", "House", "Duplex", "Mobile"
In another column I have quadrant_main with values: "NW", "SW", "NE", "SE".
My code works when there is only 1 quadrant selected, but when I select multiple quadrants, I seem to get results which includes ALL the property types from the second or third or 4th quadrant, instead of only "Apartment" and "Duplex" or whatever types the user selects... Any help will be appreciated! thx in advance.
My controller function looks like this:
public function quadrants()
{
$input = \Request::all();
$currentPage = null;
$column = "price";
$order = "desc";
//
// Looks like the input is like 0 => { key: value } ...
// (an Array of key/value pairs)
$q = Listing::where('status','=','Active')->where(function($query) {
$input = \Request::all();
$currentPage = null;
$typeCount = 0;
$quadrantCount = 0;
foreach( $input as $index => $object ) {
$tempObj = json_decode($object);
$key = key((array)$tempObj);
$val = current((array)$tempObj);
if ( $key == "type" ) {
if ( $typeCount > 0 ) {
$query->orWhere('type', '=', $val );
}
else {
$query->where('type', '=', $val );
$typeCount++;
}
}
if ( $key == "quadrant_main" ) {
if ( $quadrantCount > 0 ) {
$query->orWhere('quadrant_main', '=', $val );
}
else {
$query->where('quadrant_main', '=', $val );
$quadrantCount++;
}
}
// else {
// $query->orWhere($key,$val);
// }
}
if( $currentPage ) {
//Force Current Page to Page of Val
Paginator::currentPageResolver(function() use ($currentPage) {
return $currentPage;
});
}
});
$listings = $q->paginate(10);
return $listings;
Looking at your question, its a bit confusing and not much is given to answer definitely. Probable causes of your troubles may be bad data in database, or maybe corrupted input by user.
Disclaimer: Please note that chances are my answer will not work for you at all.
In that case please provide more information and we will work things
out.
There is one thing that I think you have overlooked and thus you are getting awry results. First let me assume a few things.
I think a sample user input should look like this:
array(
0: '{type: Apartment}',
1: '{type: Duplex}',
2: '{quadrant_main: NW}',
3: '{quadrant_main: SW}',
)
What the user meant was give me any apartment or duplex which belongs in NW or SW region.
So after your loop is over, the final SQL statement should be something like this:
Oh and while we are at SQL topic, you can also log the actual
generated SQL query in laravel so you can actually see what was the
final SQL getting generated. If you can post it here, it would help a
lot. Look here.
select * from listings where status = 'Active' and (type = 'Apartment' or type = 'Duplex' and quadrant_main = 'NW' or quadrant_main = 'SW');
What this query will actually produce is this:
Select any listing which is active and:
1. Type is an apartment, or,
2. Type is a duplex, or,
3. Quadrant is SW, and,
4. Quadrant is NW
So assuming you have a database like this:
id|type|quadrant_main
=====================
1|Apartment|NW
2|Apartment|SW
3|Apartment|NE
4|Apartment|SE
5|Duplex|NW
6|Duplex|SW
7|Duplex|NE
8|Duplex|SE
9|House|NW
10|House|SW
11|House|NE
12|House|SE
You will only receive 1, and 5 in the result set. This result set is obviously wrong, plus it is depended on NW because that was the and condition.
The correct SQL query would be:
select * from listings where status = 'Active' and (type = 'Apartment' or type = 'Duplex') and (quadrant_main = 'NW' or quadrant_main = 'SW');
So structure your L5 app such that it produces this kind of SQL query. Instead of trying to cram everything in one loop, have two loops. One loop should only handle type and another loop should only handle quadrant_main. This way you will have the necessary and condition in the right places.
As a side note:
Never directly use user input. Always sanitize it first.
Its not a best practice to put all your logic in the controller. Use repository pattern. See here.
Multiple where clauses are generally applied via Criteria. Check that out in the above linked repository pattern.
You code logic is very complicated and utterly un-necessary. Instead of sending JSON objects, simply send the state of checkboxes. Don't try to generalize the function by going in loop. Instead handle all checkboxes one by one i.e. is "Apartments" selected, if yes, add that to your clause, if not, don't add.