I would like to delete all the records from database matching a particular user_id in Symfony2.
$em = $this->getDoctrine()->getManager();
$user_service = $em->getRepository('ProjectTestBundle:UserService')
->findByUser($this->getUser()->getId());
This might return a few matching objects, so when I run:
$em->remove($user_service);
$em->flush();
an error occurs:
EntityManager#remove() expects parameter 1 to be an entity object, array given.
How do I remove all records (objects) matching a particular condition?
Btw, when I run an equivalent sql statement in mysql, it works perfectly.
Why don't you just loop through the objects array?
$user_services = $em->getRepository('ProjectTestBundle:UserService')
->findByUser($this->getUser()->getId());
foreach ($user_services as $user_service) {
$em->remove($user_service);
}
$em->flush();
You could also use something like this:
$user_services = $em->getRepository('ProjectTestBundle:UserService')->findByUser($this->getUser()->getId());
array_walk($user_services, array($this, 'deleteEntity'), $em);
$em->flush();
Then add this method in your controller:
protected function deleteEntity($entity, $key, $em)
{
$em->remove(entity);
}
Or simply use:
$user_services = $em->getRepository('ProjectTestBundle:UserService')->findByUser($this->getUser()->getId());
$this->deleteEntities($em, $user_services);
$em->flush();
...
protected function deleteEntities($em, $entities)
{
foreach ($entities as $entity) {
$em->remove($entity);
}
}
Note that when using Propel and the PropelBundle, the PropelObjectCollection implements a delete() function so you don't have to do this loop by hand.
You can also make use of doctrine query builder delete method.
public function deleteAllByUser(UserInterface $user)
{
$query = $this->createQueryBuilder('related_entity')
->delete()
->andWhere('related_entity.user = :user')
->setParameter('user', $user)
->getQuery();
return $query->execute();
}
If you return one object, you just need to write:
->findOneByUser,
You won't need a foreach loop. If your return is an array, then you need to do ->findByUser and write a for loop:
foreach ($entities as $entity)
{
//do something
}
Related
Is it possible to append multiple where clause and make them orWhere clause?
This is what I mean:
public function call()
{
$pageTypes = ["page_user", "guest_user" ...etc];
$appendQuery = [];
// in here, the query is always where making the sql query "page_type LIKE %page_user% and page_type LIKE %guest_user%"
// my written mysql query here might be written wrong but I hope you get the idea.
// I want to change the subsequent query to orWhere
foreach ($pageTypes as $i => $pageType) {
array_push($appendQuery, function ($query) use ($pageType) {
return $this->addPageTypeQuery($query, $pageType);
});
}
}
public function addPageTypeQuery($query, $pageType)
{
return $query->where('page_type', 'LIKE', $pageType);
}
Though I can manually create a function with query where・orWhere, but if there is a laravel/php way to do this, that would help.
Note I am unable to change the contents of addPageTypeQuery function. I know I can add an input parameter inside then create an ifelse/switch statement to determine what the desired return data but I cannot do so since my PM will not allow it.
Extra note I forgot I can use whereIn but currently there is no whereIn function in the current repository.php file I am working and I cannot add/edit functions because of PM. I might create my own function of whereIn clause or hack the appenedQuery and manually change the where to orWhere (Bad practice) Too complicated. Will create a whereIn instead.
orWhere() just uses where() in the background so you should be able to do it. The signature for the where method is
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
...
}
orWhere is pretty similar
public function orWhere($column, $operator = null, $value = null)
{
...
return $this->where($column, $operator, $value, 'or');
}
With this in mind, I think you only need a way to pass an extra argument to your addPageTypeQuery method.
public function addPageTypeQuery($query, $pageType, $boolean = 'and')
{
return $query->where('page_type', 'LIKE', $pageType, $boolean);
}
foreach ($pageTypes as $i => $pageType) {
array_push($appendQuery, function ($query) use ($pageType) {
if (/* should be an OR query */) {
return $this->addPageTypeQuery($query, $pageType, 'or');
}
return $this->addPageTypeQuery($query, $pageType);
});
}
I am trying to build a Laravel Nova Filter for my Model "Question", based on data from two different tables. I have figured out the exact SQL query I would need to use, but cannot figure out how to use it in the apply() method of the Nova filter.
Table "questions" columns:
id, ...
Table "themes" columns:
id, question_id, theme_id
The SQL Query I am trying to use:
SELECT questions.*
From questions
Inner JOIN themes on questions.id=themes.question_id
WHERE themes.theme_id = 3 (value taken from the Nova Filter options)
My Filter in Laravel Nova QuestionsThemeFilter.php:
class QuestionsThemeFilter extends BooleanFilter
{
public $name = 'filter by theme';
public function apply(Request $request, $query, $value) {
// This is what I'm missing...
}
public function options(Request $request) {
$themes = Themes::all();
$themesArray = array();
foreach($themes as $item) {
$themesArray[$item['title']] = strval($item['id']);
}
return $themesArray;
}
}
So, how exactly should I do this?
Other question: Since I am a beginner in Laravel, how exactly should I start debugging this? Using dd() doesn't work (the filter just breaks) and I don't know how I can debug this. Is there a way to dump some values from the apply method (for example the $value)?
Thanks in advance for any help, highly appreciated :-D
Check the comments in the code
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
class QuestionsThemeFilter extends BooleanFilter
{
public function apply(Request $request, $query, $value) {
// Since you are using BooleanFilter $value will be an associate array with true/false value
$selectedThemes = Arr::where($value, function ($v, $k) {
return $v === true;
});
$selectedThemeIds = array_keys($selectedThemes)
// For debugging print some logs and check
Log::info($value)
Log::info($selectedThemeIds);
return $query->join('themes', 'questions.id', '=', 'themes.question_id')
->whereIn('themes.theme_id', $selectedThemeIds);
// If you are using normal filter then below code will be enough
/*
return $query->join('themes', 'questions.id', '=', 'themes.question_id')
->where('themes.theme_id', $value);
*/
}
public function options(Request $request) {
// You can use Laravel collection method pluck
return Themes::pluck('id', 'title')->all();
}
}
References:
Pluck
Boolean Filter
Arr::where
I am trying to retrieve the data on my wishlist table, for a particular user, so far it only retrieves the first data on the table, just returning one array instead of the three in the table with same user id
public function getWishlistByUserId($id){
$wishlists = Wishlist::where('userId', $id)->get();
foreach($wishlists as $wishlist){
$products = Product::where('id', $wishlist->productId)->get();
return $products;
}
}
It happens because the foreach loop returns a value during the first iteration. Place your return statement outside the loop. Also you could improve your performence by making use of relationships.
An example could be:
// Product.php
public function wishlists()
{
return $this->hasMany(Wishlist::class);
}
// Your method
public function getWishlistByUserId($id)
{
return Product::whereHas('wishlists', function ($query) use ($id) {
$query->where('userId', $id);
});
}
Ideally this is n+1 situation
So i will suggest to use laravel relationship like:
in your whishlist model
public function product(){
return $this->hasMany(Product::class,'productId','id');
}
get data with relationship
public function getWishlistByUserId($id){
$wishlists = Wishlist::with('product')->where('userId', $id)->get();
}
I was finally able to get it working this way, i just pushed the result into an array, and then returned it outside the loop, thanks everyone for your help
public function getWishlistByUserId($id){
$wishlists = Wishlist::where('userId', $id)->get();
$wishlist = [];
foreach($wishlists as $wish){
$product = Product::where('id', $wish->productId)->get();
array_push($wishlist, $product);
}
return $wishlist;
}
I wrote an api with a function that sets notification as read by passing it's id.
But also, there should be an option to pass array of ids there, to mark several at once as read. I should extend function so that it handles the case where $this>data['id'] is an array.
Is this the right way?
My Service:
public function read($id = []){
$notification = $this->getRepository()->findBy([
'id' => $id
]);
if($notification) {
$notification[0]->setRead(new \DateTime());
$this->em->flush();
}
}
My Controller:
public function readAction()
{
$this->requirePostParams(['id']);
$this->get('app')->read(
$this->data['id']
);
return $this->success();
}
You can indeed pass an array of id values to \Doctrine\ORM\EntityRepository::findBy(); e.g:
$notifications = $this->getRepository()->findBy([
'id' => [1, 2, 3] // etc.
]);
However, since findBy() can return multiple results, it will return an array (or array-like object like Doctrine\ORM\PersistentCollection). Therefore you should iterate over your result set:
foreach ($notifications as $notification) {
$notification->setRead(new \DateTime());
}
$this->em->flush();
Additionally, it's a matter of taste to some degree but you may want to make your API more explicit and create separate methods for a single action versus a group action; e.g:
public function read(int $id)
{
//in this scenario you are searching for one notification
// only so you can use `findOneBy()` instead
$notification = $this->getRepository()->findOneBy(['id' => $id]);
$notification->setRead(new \DateTime());
$this->em->flush();
}
public function readMany(array $ids)
{
$notification = $this->getRepository()->findBy(['id' => $ids]);
foreach ($notifications as $notification) {
$notification->setRead(new \DateTime());
}
$this->em->flush();
}
As pointed out by #Yoshi, read() could also be neatly implemented as:
public function read(int $id)
{
$this->readMany([$id]);
}
Hope this helps :)
These are all optional fields, so will I have to write multiple queries with conditions or is there any way to handle this using Laravel? What will be the query looks like?
Thanks
It depends a bit on how the filters are submitted, but you can do one of the following two things (and probably a gazillion more...):
public function listCars(Request $request)
{
$cars = Car::when($request->get('make'), function ($query, $make) {
$query->where('make', $make);
})
->when($request->get('model'), function ($query, $model) {
$query->where('model', $model);
})
->...
->get();
// do what you have to do
}
So you are basically wrapping your query builder calls in when($value, $callback), which will only execute $callback if $value evaluates to true. When you retrieve a not set parameter with $request->get('parameter'), it will return null and the callback is not executed. But be careful, if $value is 0 it will also not execute the callback. So be sure you don't have this as an index.
As alternative to this, you can also do the same thing but with a bit less eloquent expressions...
public function listCars(Request $request)
{
$query = Car::query();
if($request->filled('make')) {
$query->where('make', $request->get('make'));
}
if($request->filled('model')) {
$query->where('model', $request->get('model'));
}
// some more filtering, sorting, ... here
$cars = $query->get();
// do what you have to do
}
Here is a working example of something similar query i have in my app.
$filters = $vehicle->newQuery();
if (!empty($request->make)) {
$filters->where('make_id', $request->make);
}
if (!empty($request->carmodel)) {
$filters->where('carmodel_di', $request->carmodel);
}
if (!empty($request->year)) {
$filters->where('year_id', $request->year);
}
if (!empty($request->engine)) {
$filters->where('engine_id', $request->engine);
}
if (!empty($request->price)) {
$filters->where('price_id', $request->price);
}
$cars = $filters->latest()->paginate(50);
and now push the $cars variable to view. I hope this works for you or atleast gives you an idea on how to proceed
here is a simple way, you can also make the joins conditional inside the ->when() condition, if you are in Laravel version > 5.4, use $request>filled() instead of $request->has()
public function listCars(Request $request)
{
$cars = Car::when($request->has('make'), function ($query)use($request) {
$query->join('maker','car.makerId','=','maker.id')
->where('make', $request->input('make'));
})
->when($request->has('model'), function ($query)use($request) {
$query->where('model', $request->input('model'));
})
->...
->get();
// you can even make the join conditionaly,
}
$fiterItem = ['make','model','year','engine','price'];
$filters = $vehicle->newQuery();
foreach ($filter as $item) {
if ($r->filled($item)) {
$list->where($item, $r->query($item));
}
}
$list = $filters->paginate(20);