I have two entities User and Article with many-to-many relation as Article can have many authors.
class User
{
/** #var string */
public $name;
/** #var Collection<Article> */
public $articles;
}
class Article
{
/** #var string */
public $title;
/** #var Collection<User> */
public $authors;
}
How I can find all Articles with specified (co)author using DQL?
Use MEMBER OF expression.
Your DQL query could like like
SELECT art FROM Article art WHERE :user MEMBER OF art.authors
or using query builder
$queryBuilder = $repository->createQueryBuilder("art");
$queryBuilder->where(":user MEMBER OF art.authors");
Alternatively you can join and filter collection
SELECT art FROM Article art JOIN art.authors aut WHERE aut = :user
or
$queryBuilder = $repository->createQueryBuilder("art");
$queryBuilder->join("art.authors", "aut");
$queryBuilder->where("aut = :user");
Use Query Builder
Summary
$qb->expr()->isMemberOf(':user', 'a.authors')
Solution
src/Repository/ArticleRepository.php
/**
* #param User $author
* #return Article[] List of articles filtered by $author
*/
public function findByAuthor(User $author): array
{
$qb = $this->createQueryBuilder('a');
$qb->setParameter('user', $author);
$qb->where($qb->expr()->isMemberOf(':user', 'a.authors'));
return $qb->getQuery()->getResult();
}
Example use
src/Controller/ArticleController.php
/**
* #Route("/article/{id<\d+>}", name="show-articles-by-author")
* #param ArticleRepository $articleRepository
* #param User $author
*/
public function showArticlesFromAuthor(ArticleRepository $articleRepository, User $author)
{
$articles = $articleRepository->findByAuthor($author);
return $this->render('articles.html.twig', [
'articles' => $articles,
'author' => $author->getName()
]);
}
templates/articles.html.twig
<h1>Articles from {{ author }}</h1>
<li>
{% for article in articles %}
<ul>
{{ article.title }}
</ul>
{% endfor %}
</li>
Related
I installed FOSUserbundle and HWI Oauth bundle.
My problem is: I want to access data from my user entity that is stored in a relation. I'd like to access the data from the fields social_network_slug and social_identifier from UserInSocialNetworks within the FOSUserProvider.
The idea was, that one user can have more that one social network logins. (1:n)- When I log in with my google/facebook etc login, I want to check the table user_in_social_networks if the Id with the social network already exists.
/*
* This is the User class, depending on fos_userBundle
*/
namespace AppBundle\Entity\Registration;
use Doctrine\Common\Collections\ArrayCollection as ArrayCollection;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* One User can have many social networks
* #ORM\OneToMany(targetEntity="UserInSocialNetworks", mappedBy="user", cascade={"remove"})
*/
private $socialnetworks; ....
the Entity Class to store all User's social media logins:
<?php
namespace AppBundle\Entity\Registration;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* UserInSocialNetworks
*
* #ORM\Table(name="user_in_social_networks")
* #ORM\Entity(repositoryClass="AppBundle\Repository\Registration\UserInSocialNetworksRepository")
*/
class UserInSocialNetworks
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Many Socialnetwork Logins have one User
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Registration\User", inversedBy="socialnetworks")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*
*/
private $user;
/**
* #var int
*
* #ORM\Column(name="social_network_slug", type="string", length=255, nullable=true)
*/
private $socialNetworkSlug;
/**
* #var string
*
* #ORM\Column(name="social_identifier", type="string", length=255, nullable=true)
*/
private $socialIdentifier;
The extended FOSUBUserProvider class:
<?php
namespace AppBundle\Entity\Registration;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
use Symfony\Component\Security\Core\User\UserInterface;
class FOSUBUserProvider extends BaseClass
{
/**
* {#inheritDoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
// get user_id and socialnetworkname from response
$userIdInSocialNetwork = $response->getUsername();
$socialnetwork = $response->getResourceOwner()->getName();
// Here I'd like to search for an existing $userIdInSocialNetwork
What I checked since now: I can't access the entitymanager in FOSUBUserProvider class, and I can't search that way:
$user = $this->userManager->findUserBy(array(
'socialIdentifier' => $userIdInSocialNetwork,
'social_network_slug' => $socialnetwork)
because it's a relation.
Thanks for any idea!
As you mentioned that you have extended FOSUBUserProvider i assume you have defined a new service for this, If so then you can pass doctrine's entity manager to your class #doctrine.orm.entity_manager. Following HWIOAuthBundle documentation for FOSUserBundle you can pass entity manager as
services:
my.custom.user_provider:
class: MyBundle\Security\Core\User\MyFOSUBUserProvider
arguments: ['#fos_user.user_manager', { facebook: facebook_id }, #doctrine.orm.entity_manager]
And then in your class you can use this service as
use Doctrine\ORM\EntityManager;
use FOS\UserBundle\Model\UserManagerInterface;
//.... other use statements
class FOSUBUserProvider extends BaseClass
{
private $em;
public function __construct(UserManagerInterface $userManager, array $properties, EntityManager $em)
{
$this->em = $em;
parent::__construct($userManager, $properties); /* pass dependencies to parent */
}
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$this->em->getRepository('AppBundle\Entity\Registration\UserInSocialNetworks')->findBy(....);
/* Do your stuff here */
}
}
Get error on my implementation but resolve this by adding quote to the third argument like bellow:
services:
my.custom.user_provider:
class: MyBundle\Security\Core\User\MyFOSUBUserProvider
arguments: ['#fos_user.user_manager', { facebook: facebook_id }, '#doctrine.orm.entity_manager']
I want to make a friendlist using ManyToMany self-referencing.
I followed this link and it seemms to be good. But Now, what Action should I have in my controller to :
1- get All myFriends / or all all friend of current user
2- add a firend, using a link such as "Add friend"
thank you for your time and answers
in your link my friends is a doctrine array collection, to get the friends of a user just iterate on it and to add a friend just add a friend onto this collection and save the entity (with the entity manager), maybe you ll need to add a cascade persist on the collection to add a new user as friend.
you have to add some methods like getMyFriends, addFriend and removeFriend
<?php
/** #Entity */
class User
{
// ...
/**
* Many Users have Many Users.
* #ManyToMany(targetEntity="User", mappedBy="myFriends")
*/
private $friendsWithMe;
/**
* Many Users have many Users.
* #ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
* #JoinTable(name="friends",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="friend_user_id", referencedColumnName="id")}
* )
*/
private $myFriends;
public function __construct() {
$this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
$this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getMyFriends(){
return $this->myFriends;
}
public function addFriend(User $friend){
$this->myFriends->add($friend);
}
public function removeFriend(User $friend){
$this->myFriends->removeElement($friend);
}
}
in your controller you have to implement an action with
$currentUser= $this->get('security.context')->getToken()->getUser();
$myFriends = $currentUser->getMyfriends();
$this->render('your-template.html.twig', array(
'myFriends' => $myFriends,
));
and in your twig template
<h1>My friends</h1>
<ul>
{% for friend in myFriends %}
<li>{{ friend.username }}</li>
{% endfor %}
</ul>
I am new to Laravel and I have multiple registration types I have tried to add a foreign key following the aravel 5.2 documentation and it keeps giving me errors any help would be greatly appreciated. I need to connect the registration of each type of user into the different registration types. Below I will post one of the three registration types that I have. If there is a way to only add the user information to the user table after there email has been verified that would be great. So in other words each registration (3x) I need them to fill out registration and only the user table info goes to the user table the rest would go to the other table but I want to two to be connected.
This is the migration file I am trying to get to work following the laravel 5.2 documents.
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->boolean('active')->default(false);
$table->rememberToken();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('artists');
$table->foreign('user_id')
->references('id')->on('artists')
//->onUpdate('cascade')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('users');
//$table->dropForeign('artists_user_id_foreign');
}
}
I also tried doing the same thing on the artists table and it did not work either keep getting a foreign constraint issue.
Ill need to do that process for each type of user account.
The controller is set up like this for artist
<?php
namespace App\Http\Controllers\Artist;
use App\User;
use App\Artist;
use App\Mailers\AppArtMailer;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class RegistrationController extends Controller
{
//protected $redirectTo = 'art';
/** Create a new registration instance.
*/
public function __construct()
{
//$this->middleware('artist');
}
/** show the Register page/
*
* #return \Response
*/
public function register()
{
return view('art.register');
}
/**
* Perform the registration.
*
* #param Request $request
* #param AppMailer $mailer
* #return \Redirect
*/
public function postRegister(Request $request, AppArtMailer $mailer)
{
//Validate
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:artists',
'password' => 'required'
]);
//create artist
$artist = Artist::create($request->all());
$user = User::create($request->all());
//email them
$mailer->sendEmailConfirmationTo($artist);
//$mailer->sendEmailConfirmationTo($user);
//flash
flash('Please confirm your email address.');
// redirect
return redirect()->back();
}
/**
* Confirm a user's email address.
*
* #param string $token
* #return mixed
*/
public function confirmEmail($token)
{
Artist::whereToken($token)->firstOrFail()->confirmEmail();
flash('You are now confirmed. Please login');
return redirect('artist');
}
}
I also followed the laracast ACL Roles and Permissions but am not sure how to integrate that into each of the user registrations so that each type automatically has the set roles.
Here are the different models I have each is a bit different since I am learning and playing.
Artist Model This will actually change since they can only be an artist if they are a viewer.
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use App\Http\Middleware\RedirectIfAuthenticatedArtist;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class Artist extends Model implements AuthenticatableContract, CanResetPasswordContract
{
protected $table = 'artists';
use Authenticatable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function($artist) {
$artist->token = str_random(30);
});
}
/**
* Set the password attribute.
*
* #param string $password
*/
public function setPasswordAttribute($password)
{
$this->attributes['password'] = bcrypt($password);
}
/**
* Confirm the user.
*
* #return void
*/
public function confirmEmail()
{
$this->verified = true;
$this->token = null;
$this->save();
}
}
This is my users model
<?php
namespace App;
use DB;
use Illuminate\Http\Response;
use App\Http\Controllers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\File;
use Cmgmyr\Messenger\Traits\Messagable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasRoles, Messagable;
//* The attributes that are mass assignable.
//*
// * #var array
// */
protected $fillable = [
'name', 'email', 'password',
];
public function setPasswordAttribute($password)
{
$this->attributes['password'] = bcrypt($password);
}
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
// $user->roles
}
Sponsors Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
//use App\Http\Middleware\RedirectIfAuthenticatedSponsor;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class Sponsor extends Model implements AuthenticatableContract, CanResetPasswordContract
{
protected $table = 'sponsors';
use Authenticatable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function($sponsor) {
$sponsor->token = str_random(30);
});
}
/**
* Set the password attribute.
*
* #param string $password
*/
public function setPasswordAttribute($password)
{
$this->attributes['password'] = bcrypt($password);
}
/**
* Confirm the user.
*
* #return void
*/
public function confirmEmail()
{
$this->verified = true;
$this->token = null;
$this->save();
}
}
Viewer Model
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Cmgmyr\Messenger\Traits\Messagable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use App\Http\Middleware\RedirectIfAuthenticatedViewer;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class Viewer extends Model implements AuthenticatableContract, CanResetPasswordContract
{
protected $redirectTo = 'viewer';
use Authenticatable, CanResetPassword, Messagable;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'viewers';
//protected $table = 'experience';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function experience()
{
return $this->hasOne('App\Experience');
}
public function awardExperience($points)
{
return $this->experience->award($points);
}
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(func
I'm using SonataAdmin and SonataDoctrineORMAdmin bundles to manage entities.
The problem is I can't figure out how to eager fetch the related entities in the list view and as the number of listed entities increase the number of queries executed increasing rapidly as well.
I tried adding `fetch="EAGER" to the relation annotations but the profiles show that Sonata executes the separate queries anyway.
Here's one relation worth of code:
Post
<?php
namespace Acme\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Table()
* #ORM\Entity
*/
class Post
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=255)
**/
private $name;
/**
* #ORM\ManyToMany(targetEntity="Acme\AppBundle\Entity\Tag", fetch="EAGER")
* #ORM\JoinTable(name="join_post_to_tag",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
**/
private $tags;
public function getId()
{
return $this->id;
}
public function setName($names)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setTags($tags)
{
$this->tags = $tags;
return $this;
}
public function __toString()
{
return $this->getName();
}
}
Tag
<?php
namespace Acme\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity
*/
class Tag
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="value", type="string", length=255)
*/
private $value;
public function getId()
{
return $this->id;
}
public function setValue($value)
{
$this->value = $value;
return $this;
}
public function getValue()
{
return $this->value;
}
public function __toString()
{
return ($this->getValue()) ? : '';
}
}
The first related query that is run is fetching all the posts:
SELECT DISTINCT p0_.id AS id0, p0_.id AS id1
FROM Post p0_
LEFT JOIN join_post_to_tag j1_ ON p0_.id = j1_.post_id
LEFT JOIN Tag p1_ ON p1_.id = j1_.target_id
ORDER BY p0_.id ASC
But this does not fetch the related tags or even if it does, it still queries it again:
SELECT t0.id AS id1, t0.value AS value2
FROM Tag t0
INNER JOIN join_post_to_tag ON t0.id = join_post_to_tag.tag_id
WHERE join_post_to_tag.post_id = ?
I tried to mess with the createQuery method in the admin class but could not really find a way to make the related entities fetched correctly.
Is there a way to force the list view to eager fetch the required related entities?
You are on the right track, using the createQuery($context) method.
I have achieved eager loading as following:
public function createQuery($context = 'list')
{
$query = parent::createQuery($context); // let sonata build it's default query for the entity
$rootEntityAlias = $query->getRootAlias(); // get the alias defined by sonata for the root entity
$query->join($rootEntityAlias.'.relationFieldName', 'relationFieldAlias'); // manualy define the join you need
$query->addSelect('relationFieldAlias'); // this is the key line. It is not enough to join a table. You have to also add it to the select list of the query, so that it's actualy fetched
// $query->join(...) // repeat the process of joining and selecting for each relation field you need
// $query->addSelect(...)
return $query; // return the altered query to sonata. this will only work for the "list" action.
}
If you're having trouble using this, let me know:)
Further reads on this topic:
SO question
docs
After annotating an OneToOne Unidirectional, i want to output the joined column, without using a form.
One People Entity has got a column to store the id of Country Entity.
What i can do: I can store the id of the country into the People Entity using a form with a dropdown select, which is bound to the Country Entity.
Problem: I can not enter the value country of the, hopefully correct, joined table.
People Entity:
<?php
namespace People\Entity;
use Doctrine\ORM\Mapping as ORM;
// ...
/**
* A people entity.
*
* #ORM\Entity
* #ORM\Table(name="icd_people")
* #property int $id
// ...
* #property string $ic_hq_country
*/
class People implements InputFilterAwareInterface
{
protected $inputFilter;
/**
* #ORM\Id
* #ORM\Column(type="integer");
*/
protected $id;
/**
* #ORM\Column(type="integer")
* #ORM\OneToOne(targetEntity="Country")
* #ORM\JoinColumn(name="ic_hq_country", referencedColumnName="id")
*/
protected $ic_hq_country;
// getter and setter
}
The Country Entity:
<?php
namespace People\Entity;
use Doctrine\ORM\Mapping as ORM;
//...
/**
* A Country entity.
*
* #ORM\Entity
* #ORM\Table(name="pre_country")
* #property int $id
* #property string $country
*/
class Country implements InputFilterAwareInterface
{
protected $inputFilter;
/**
* #ORM\Id
* #ORM\Column(type="integer");
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $country;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set country
*
* #param string $country
* #return Country
*/
public function setCountry($country)
{
$this->country = $country;
return $this;
}
/**
* Get country
*
* #return string
*/
public function getCountry()
{
return $this->country;
}
/**
* Convert the object to an array.
*
* #return array
*/
public function getArrayCopy()
{
return get_object_vars($this);
}
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
public function getInputFilter()
{
throw new \Exception("Not used");
}
}
The Controller Action:
public function indexAction()
{
$userid = $this->zfcUserAuthentication()->getIdentity()->getId();
return new ViewModel(array(
'pea' => $this->getEntityManager()->find('People\Entity\People', $userid),
));
}
The View giving the id of the country, but not the name:
<?php echo $this->escapeHtml($pea->ic_hq_country);?>
I actually expected something like this being possible, to output the country name and not the id:
<?php echo $this->escapeHtml($pea->country);?>
Thank you for reading, and for any help, which could lead me into the right direction!
You should not use the #Column anotation in the $ic_hq_country field of the Peopleentity.
/**
* #ORM\OneToOne(targetEntity="Country")
* #ORM\JoinColumn(name="ic_hq_country", referencedColumnName="id")
*/
protected $ic_hq_country;
like this, hopefully ic_hq_country will be a proxy to the entity instead of the id.
so in your view you can use:
<?php echo $pea->ic_hq_country->getId();?>
and also
<?php echo $this->escapeHtml($pea->ic_hq_country->getCountry());?>