So I'm using the FOSUserBundle with LDAP as my authentication method, and I'm wondering whether there is a way to remove/ignore the password field for my FOSUser entity?
I realize removal might not be ideal (in case it messes with internal logic), but the column is never used, and I'm forced to fill it when I'm creating FOSUsers from fixtures:
$user = new FOSUser();
$user->setDn($item["dn"]);
$user->setEnabled(1);
$user->setUsername($item["samaccountname"][0]);
$user->setUsernameCanonical(strtolower($item["samaccountname"][0]));
$user->setEmail($item["mail"][0]);
$user->setEmailCanonical(strtolower($item["mail"][0]));
$user->setDepartment($ldap_groups[$matches[1]]);
$user->setDepartmentDn($group);
$user->setPassword('blank'); // Is there away to avoid this?
$manager->persist($user);
You could actually remove the password, but you have to keep a getPassword() as that is defined in the UserInterface. In case you want to keep it, e.g. when you allow for multiple login types where you will need it again. I recommend setting the field as nullable. If you are using annotations it's as simple as adding nullable=true to the column:
/**
* #ORM\Column(type="string", name="password", nullable=true)
*/
So to override a column doctrine allows for Inheritance Mapping:
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="password",
* column=#ORM\Column(
* name = "password",
* type = "string",
* nullable=true
* )
* )
* })
*/
class FOSUser extends BaseUser implements LdapUserInterface
{
// Extended logic.
}
The password field still resides in BaseUser, but is overwritten by the #ORM\AttributeOverride annotation.
Related
So, I need a validation for a reservation for a sports club.
A reservation has a start and an end datetime and you can reservate for 1 or more tables.
So the Entity looks like
class Reservation
{
use TimestampAble;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="reservations")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Table", inversedBy="reservations")
*/
private $tables;
/**
* #ORM\Column(type="datetime")
*/
private $start;
/**
* #ORM\Column(type="datetime")
*/
private $end;
...
}
(Don't worry about the entity "Table" - the db table for it is named "snooker_table" ;))
Now I need to validate, that in the requested time range with the requested tables no other reservation already exist.
And this gives me headaches...
I know I can make it "manually" in the Controller Actions create / update. But I'm also using Symfonys Easy-Admin, so I need to put the code there as well.
I thought about putting the validation as an annotation directly into the entity. But I don't know where... If I put it on "$tables" I just get an ArrayCollection without the needed start and end datetimes. And it's also not an unique entity (as I need to go on a range of datetimes and so on).
So: any ideas how to achieve this in the entity directly? or at least in the form type (and for easy admin i care later)?
Thx in advance.
Ok, I'm going to do it in this way: Symfony 2 UniqueEntity repositoryMethod fails on Update Entity
Creating a repository method (not a constraint) for validation and use it for the UniqueEntity constraint on the whole entity itself. Feels a little bit dirty but ok...
Hope this works in easy admin as well.
I am using Gedmo extension in addition with Symfony 3.2 and Doctrine 2.5.6 and I'm encountering an issue. I can't make Gedmo\Blameable and UniqueEntity constraint work together. Indeed, the blamed field is still null at validation time. Is there any way to make it work or a possible work-around ?
Here is my entity
/**
* #UniqueEntity(
* fields={"author", "question"},
* errorPath="question",
* message="This author already has an answer for that Question"
* )
* #ORM\Entity
*/
class TextAnswer
{
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* #Gedmo\Blameable(on="create")
*/
private $author;
/**
* #Assert\NotNull()
* #ORM\ManyToOne(targetEntity="Question", inversedBy="textAnswers")
* #ORM\JoinColumn(name="question_id", referencedColumnName="id")
*/
private $question;
}
Thanks
EDIT : SOLUTION
Rather than manually setting the user (which removes Gedmo\Blameable interests), I created my own entity validator.
I give it doctrine and token storage as arguments so it can make a query on db to validate my criteria with the currently connected user (that will be later used by Gedmo\Blameable).
The BlameableListener is invoked during the Doctrine's flush operation, which normally happens after the entity has been validated. That's why $author is null at validation time.
The most straightforward workaround is to set $author yourself beforehand.
There are following entities: Zone, ZoneRecord
ZoneRecord has a Method validate() to validate against all other ZoneRecord's of related Zone.
Now I want to check / validate each ZoneRecord (the ones which are saved already plus the ones which are added by Zone->addRecord(ZoneRecord) on runtime) which is related to the Zone if Zone gets saved.
Right now I have a PreFlush Lifecyclecallback ZoneRecord->validate where I trigger this->getZone->getRecords(): this methods gives me only the already saved entities which are in db.
How can I get ALL related Entities of Zone (the saved from DB and the dynamicly added)?
The Problem seems to be in Doctrine internal.
I use InheritanceType "JOINED".
The annotation in Zone looks like:
/**
* #var ZoneRecord[]
* #ORM\OneToMany(targetEntity="Application\Entity\ZoneRecord", mappedBy="zone", cascade={"all"}, orphanRemoval=true)
*/
protected $records;
/**
* #var ZoneRecordA[]
* #ORM\OneToMany(targetEntity="Application\Entity\ZoneRecordA", mappedBy="zone", cascade={"all"}, orphanRemoval=true)
*/
protected $recordsa;
The annotation from ZoneRecord:
/**
* #ORM\Entity
* #ORM\Table(name="zonerecords")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({
* "A" = "Application\Entity\ZoneRecordA"
* })
* #ORM\HasLifecycleCallbacks
*/
abstract class ZoneRecord
The annotation from ZoneRecordA:
/**
*
* #ORM\Entity
* #ORM\Table(name="zonerecords_a")
*
*/
class ZoneRecordA extends ZoneRecord
If I add or remove a ZoneRecord from Zone via Zone->addRecordA(ZoneRecord) / Zone->removeRecordA(ZoneRecord) it will only be removed from the protected $recordsa; stack. The protected $records; will be untouched until the entity is successfully flushed and reloaded.
It seems that Doctrine does internal differ between ZoneRecord and ZoneRecordA although they are marked as JOINED and ZoneRecordA extends ZoneRecord.
This is especially a problem if you want to delete Records - right now I use both delete Methods (Zone->removeRecord(ZoneRecordA) and Zone->removeRecordA(ZoneRecordA)) to be sure the Entity is removed from the EntityManager.
This strange behaviour also occurs if you add entitys to Zone via addRecord() or addRecordA(), they are used as different collections until they are flushed and reloaded.
I got a bit stuck with multiple mappings of the same object in Doctrine. The app is build on Symfony btw, hence the slightly different annotations.
Basically I have the following objects:
Organisation: an umbrella holding attributes about an organisation
Department: a department within the organisation
User: a generic user object
Those objects are related as follows:
An organisation always has one and only one owner, which is a User
An organisation has many members, which are all User's
A department consists of many User's, but only members of the Organisation the Department is a part of are allowed
I'm a bit stuck at the third requirement... First of all, this is how my objects more or less look like atm:
/**
* #ORM\Entity
* #ORM\Table(name="organisations")
*/
class Organisation
{
// ...
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="organisation")
*/
private $owner;
/**
* ORM\OneToMany(targetEntity="User", mappedBy="organisation")
*/
private $members
}
/**
* #ORM\Entity
* #ORM\Table(name="departments")
*/
class Department
{
// ...
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="departments")
*/
private $members
/**
* #ORM\ManyToOne(targetEntity="Organisation", inversedBy="departments")
*/
private $organisation;
}
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
// ...
/**
* The organisation this user "owns"
*
* #ORM\OneToOne(targetEntity="Organisation", mappedBy="owner", nullable=true)
*/
private $owning_organisation;
/**
* #ORM\ManyToOne(targetEntity="Organisation", inversedBy="members")
*/
private $organisations;
/**
* #ORM\ManyToMany(targetEntity="Department", inversedBy="members")
* #ORM\JoinTable(name="users_departments")
*/
private $departments;
}
Now this basically works, if and only of in the controllers I do all the checking (something like (if( $user->isPartOfOrganisation($department-getOrganisation()) { $department->addMember($user); }).
But is there a way to restrict possible object associations on design level? So basically what I want is that if a user is added to a department, it is solely possible if the user is already part of the organisation the department is also a part of. Or should I do the check in the addMember() method of the Department object? I can imagine (but cannot find it) that there is some kind of a subset-restriction (Department::members is subset of Organisation::members).
To implements this check low-level as possible (nearest to the db) I think the only solution is a Doctrine Event Listener that in the pre-persist event check for your custom constraint. Read more about Doctrine Event System .
BTW I think you can manage this situation in a more simply manner: I suggest you to incapsulate the business logic into a service (so you can reuse it more simply) and use it in a custom validator that you will use in the form where you manage this situation.
Let me know if you need more tips to develop one of this solutions or if you found something more useful.
Hope this help
As I am creating a web application that will be used in research on patients in the health domain, I want all my users to be completely anonymous. Can I in a simple way get rid of the email and email_canonical fields without rewriting stuff in the bundle itself, for instance by doing something to my User Class in my own bundle?
EDIT: I did this:
/**
* #ORM\Column(nullable=true)
**/
protected $email;
/**
* #ORM\Column(nullable=true)
**/
protected $emailCanonical;
In my User entity class. Bu twhen I do php app/console doctrine:schema:update --force i get
[Doctrine\ORM\Mapping\MappingException]
Duplicate definition of column 'email' on entity 'Pan100\MoodLogBundle\Enti
ty\User' in a field or discriminator column mapping.
EDIT 2: Forgot to say this is done in a class extending the FOUserBundle's model class User as BaseUser...
OK!
I did:
...
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="email", column=#ORM\Column(nullable=true)),
* #ORM\AttributeOverride(name="emailCanonical", column=#ORM\Column(nullable=true, unique=false))
*
* })
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
...(code emitted)
Later I will find out if more is needed - I will probably have to override some of the FOSUserBundle methods for creating users. At least the "php app/console fos:user:create testuser" command requires an email... But it does not have to be unique anymore, so if I am hindered later I can just add the string "none" or something...