I need to provide a webservice which returns articles.
I want to include the user relationship in that result to avoid my clients to call another method to load the user object.
I use an Array Result because I want a collection of array (I think it's better to work with) so I wish I could eager load my user.
I tried:
* #ManyToOne(targetEntity="\My\Model\User\User", fetch="EAGER")
But it doesn't look to work.`
Edit, some code:
public function getPublishedArticles($page, $count, $useArrayResult = false) {
$qb = $this->createQueryBuilder('a');
$qb->where('a.status = :status')
->orderBy('a.published_date', 'DESC')
->addOrderBy('a.creation_date', 'DESC')
->setParameter('status', Article::STATUS_PUBLISHED )
->andWhere('a.published_date <= :date')
->setParameter('date', date('Y-m-d'));
}
$adapter = new PaginationAdapter($qb->getQuery());
$adapter->useArrayResult($useArrayResult);
$paginator = new \Zend_Paginator($adapter);
$paginator->setItemCountPerPage($itemCount)
->setCurrentPageNumber($page);
return $paginator;
}
And I call this method with the $useArrayResult flag sets to TRUE
When you're using DQL query you have add JOIN clause to join related entities:
$qb->createQueryBuilder('a')
->addSelect('u')
->join('a.user', 'u')
...
fetch="EAGER" and fetch="LAZY" are being used when you're fetching entities using EntityManager, ie:
$article = $em->find('Entity\Article', 123);
Related
This is my first post. I am in a trouble in my laravel project
Here is my data table.
I have student Id like 1,2,3. every students have multiple results followed by courses.
I need to arrange them like that
I tried groupby and got this result
Is there any possible way to arrange them according to students.
Thank You
code: controller:
public function notification()
{
$auth_id = Auth::user()->id;
$teacher = Teacher::where('user_id', $auth_id)->first();
$teacher_id = ($teacher->id);
$batch = Batch::where('teacher_id', $teacher_id)->first();
$courses = AssignCourses::with('course')
->where('semester_id', $batch->semester_id)
->get();
$current_semester_results = Result::with(['student', 'course'])
->where('semester_id', $batch->semester_id)
->get()
->groupBy('student.id');
$batch_students = Student::with('result')
->where('semester_id', $batch->semester_id)
->get();
return view('users.teacher.my_batch.notification', compact(['current_semester_results', 'courses', 'batch_students']));
}
Just use the $batch_students and apply any aggregations on your PHP code, it is easier to do it.
$batch_students = Student::with('result')
->where('semester_id', $batch->semester_id)
->get();
$batch_students_grouped = $batch_students->groupBy('result.student_id');
Note: I could not test since I don't have the tables, so you might need to change the student_id nest/access index in the last line of code.
you can print out your $batch_students_grouped->all() and see how you should iterate your data and show it in frontend.
Hi I am trying to get customer _id from different tables Purchase order ,Sale Order and Consignments
Then I am looping through these Ids . Method I am using for this purpose is working perfectly but . I am afraid if there is a lot of data this method may get failed. Here is my method .
$consignmentCustomerIds = Consignment::select('customer_id')->where('is_repeat', 0)->whereDate('created_at','>',date('2021-03-06'))->whereRaw('(is_group = "parent" or is_group is null)')->where('finalize', 0)->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$poCustomerIds = PurchaseOrder::select('customer_id')->whereDate('created_at','>',date('2021-03-06'))->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$soCustomerIds = SaleOrder::select('customer_id')->whereDate('created_at','>',date('2021-03-06'))->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$spCustomerIds = StoragePeriod::select('customer_id')->whereDate('created_at','>',date('2021-03-06'))->where('invoice_id', null)->distinct()->pluck('customer_id')->toArray();
$ids = array_merge($consignmentCustomerIds, $poCustomerIds, $soCustomerIds, $spCustomerIds);
$customers = Customer::whereIn('id', $ids)->get();
foreach ($customers as $customer) {
CreateInvoiceOneByOne::dispatch($customer)->onQueue('invoice');
}
Is there any better way of doing so?
The main thing is to change ->get() to ->cursor() in the iteration:
// $customers = Customer::whereIn('id', $ids)->get();
$customers = Customer::whereIn('id', $ids)->cursor();
The cursor method may be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records.
More info: https://laravel.com/docs/8.x/eloquent#cursors
IF YOUR RELATIONS ARE SET PROPERLY
I suggest to reduce database query. You can do this by chaning whereHas and orWhereHas within the customer request.
Querying Relationship Existence
$date = date('2021-03-06');
$customers = Customer::whereHas('consignment', function($query) use($date) {
$query->where('is_repeat', 0)->whereDate('created_at','>',$date)->whereRaw('(is_group = "parent" or is_group is null)')->where('finalize', 0)->where('invoice_id', null);
})->orWhereHas('purchase_order', function($query) use($date) {
$query->whereDate('created_at','>',$date)->where('invoice_id', null);
})->orWhereHas('sale_order', function($query) use($date) {
$query->whereDate('created_at','>',$date)->where('invoice_id', null);
})->orWhereHas('storage_period', function($query) use($date) {
$query->whereDate('created_at','>',$date)->where('invoice_id', null);
})->get();
foreach ($customers as $customer) {
CreateInvoiceOneByOne::dispatch($customer)->onQueue('invoice');
}
I set the $date variable before the query, so this way you can manipulate it at one place.
P.S. I am currently assuming the name of the relations.
The dapper tutorial gives this example to help a user with Multi Mapping (One to Many)
While this works I am curious why they have you store the orders in the dictionary but then in the end they use a linq.Distinct() and return from the list. It seems like it would be cleaner to just return the ordersDictionary.Values as the dictionary logic ensures no duplicates.
//Tutorial
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
Dictionary<int,Order> orderDictionary = new Dictionary<int, Order>();
List<Order> list = connection.Query<Order, OrderDetail, Order>(sql, (order, orderDetail) =>
{
if (!orderDictionary.TryGetValue(order.OrderID, out Order orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
}, splitOn: "OrderID")
.Distinct()
.ToList();
return list;
}
//my suggestion
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
Dictionary<int,Order> orderDictionary = new Dictionary<int, Order>();
//change 1 no need to store into list here
connection.Query<Order, OrderDetail, Order>(sql, (order, orderDetail) =>
{
if (!orderDictionary.TryGetValue(order.OrderID, out Order orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
}, splitOn: "OrderID"); //change 2 remove .Distinct().ToList()
return orderDictionary.Values.ToList(); //change 3 return dictionaryValues
}
I'm the author of this tutorial: https://dapper-tutorial.net/query#example-query-multi-mapping-one-to-many
why they have you store the orders in the dictionary
A row is returned for every OrderDetail. So you want to make sure to add the OrderDetail to the existing Order and not create a new one for every OrderDetail. The dictionary is used for performance to check if the Order has been already created or not.
it would be cleaner to just return the ordersDictionary.Values
How will your query return dictionary values?
Of course, if you are in a method such as yours, you can do
var list = orderDictionary.Values;
return list;
But how to make this Connection.Query return dictionary values? An order is returned for every row/OrderDetail, so the order will be returned multiple times.
Outside the Query, your dictionary solution works great and is even a better solution for performance, but if you want to make your Query return the distinct list of orders without using Distinct or some similar method, it's impossible.
EDIT: Answer comment
my suggestion return orderDictionary.Values.ToList(); //change 3 return dictionaryValues
Thank you for your great feedback, it's always appreciated ;)
It would be weird in a tutorial to use what the query returns when there is no relationship but use the dictionary for one to many relationships
// no relationship
var orders = conn.Query<Order>("", ...).Distinct();
// one to many relationship
conn.Query<Order, OrderDetail>("", ...);
var orders = orderDictionary.Values.ToList();
Your solution is better for performance the way you use it, there is no doubt about this. But this is how people usually use the Query method:
var orders = conn.Query("", ...).Distinct();
var activeOrders = orders.Where(x => x.IsActive).ToList();
var inactiveOrders = orders.Where(x => !x.IsActive).ToList();
They use what the Query method returns.
But again, there is nothing wrong with the way you do it, this is even better if you can do it.
I am attempting to sort an ArrayCollection by a specific field. The ArrayCollection is an array of courses. In the Course entity there is a method called isLive which returns a boolean.
I would like to sort this collection to have the "live" courses at the beginning of the array, so that's the courses that return true from a isLive call.
This is the code I have at present, but the first entry in the $sorted array is a non-live course.
$iterator = $this->courses->getIterator();
$iterator->uasort(function ($a, $b) {
if ($a->isLive() == $b->isLive()) {
return 0;
}
return ($a->isLive() < $b->isLive()) ? -1 : 1;
});
$sorted = new ArrayCollection(iterator_to_array($iterator));
It looks like a good use case for Doctrine Criteria. They allow to filter/sort ArrayCollections, either in memory if the collection is already loaded, either by adding a WHERE / ORDER BY SQL clause next time the collection will be loaded from the database. So that's pretty optimized!
Code should look like something like this, assuming you have a live field behind isLive():
$criteria = Criteria::create()
->orderBy(["live" => Criteria::DESC])
;
$sorted = $this->courses->matching($criteria);
For the entity, do this: add an OrderBy annotation to the property.
/**
* #OneToMany(targetEntity="Course")
* #OrderBy({"live": "ASC"})
*/
private $courses;
I got to a solution with the use of uasort and array_search as below:
/**
* #return ArrayCollection
*/
public function getCoursesSortedByLive(): ArrayCollection
{
$coursesIterator = $this->courses->getIterator();
$sortOrder = [true];
$coursesIterator->uasort(function ($a, $b) use ($sortOrder) {
return array_search($a->isLive(), $sortOrder) - array_search($b->isLive(), $sortOrder);
});
return new ArrayCollection(iterator_to_array($sitesIterator));
}
I'm trying to figure out how to execute a custom query with Castle ActiveRecord.
I was able to run simple query that returns my entity, but what I really need is the query like that below (with custom field set):
select count(1) as cnt, data from workstationevent where serverdatetime >= :minDate and serverdatetime < :maxDate and userId = 1 group by data having count(1) > :threshold
Thanks!
In this case what you want is HqlBasedQuery. Your query will be a projection, so what you'll get back will be an ArrayList of tuples containing the results (the content of each element of the ArrayList will depend on the query, but for more than one value will be object[]).
HqlBasedQuery query = new HqlBasedQuery(typeof(WorkStationEvent),
"select count(1) as cnt, data from workstationevent where
serverdatetime >= :minDate and serverdatetime < :maxDate
and userId = 1 group by data having count(1) > :threshold");
var results =
(ArrayList)ActiveRecordMediator.ExecuteQuery(query);
foreach(object[] tuple in results)
{
int count = (int)tuple[0]; // = cnt
string data = (string)tuple[1]; // = data (assuming this is a string)
// do something here with these results
}
You can create an anonymous type to hold the results in a more meaningful fashion. For example:
var results = from summary in
(ArrayList)ActiveRecordMediator.ExecuteQuery(query)
select new {
Count = (int)summary[0], Data = (string)summary[1]
};
Now results will contain a collection of anonymous types with properties Count and Data. Or indeed you could create your own summary type and populate it out this way too.
ActiveRecord also has the ProjectionQuery which does much the same thing but can only return actual mapped properties rather than aggregates or functions as you can with HQL.
Be aware though, if you're using ActiveRecord 1.0.3 (RC3) as I was, this will result in a runtime InvalidCastException. ActiveRecordMediator.ExecuteQuery returns an ArrayList and not a generic ICollection. So in order to make it work, just change this line:
var results = (ICollection<object[]>) ActiveRecordMediator.ExecuteQuery(query);
to
var results = (ArrayList) ActiveRecordMediator.ExecuteQuery(query);
and it should work.
Also note that using count(1) in your hql statement will make the query return an ArrayList of String instead of an ArrayList of object[] (which is what you get when using count(*).)
Just thought I'd point this out for the sake of having it all documented in one place.