I'm developing my first Symfony2 Bundle "distributable" by packagist.org and have a few questions regarding how to relate the "user entity" from: "the actual project", to: "this external bundle".
What I do:
I created a GIT repository specifically for my bundle.
I have created and configured the composer.json file, as explained in packagist.org
Set the user entity from the general config.yml /app/config/config.yml
to send as parameter to the bundle .
// KDRMKLabs\TicketBundle\DependencyInjection\Configuration.php
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('kdr_ticket');
$rootNode->children()
->scalarNode('user_class')->isRequired()->cannotBeEmpty()->end()
->end()
;
return $treeBuilder;
}
}
# app\config\config.yml
kdr_ticket:
user_class: AppBundle\Entity\User
// KDRMKLabs\TicketBundle\DependencyInjection\KDRTicketExtension.php
class KDRTicketExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
$container->setParameter('kdr_ticket.model.user.class', $config['user_class']);
}
}
4. Ticket Entity
/**
* Ticket
*
* #ORM\Table(name="kdr_ticket")
* #ORM\Entity
*/
class Ticket
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* How I can tell to Doctrine that this object is the specified in config.yml ?
*/
private $user;
...
}
Using the User Entity when you install the bundle or when the database is updated for the first time after installation of the bundle
How Can I use the "user entity" specified in config.yml to create relations of the form "ManyToOne" when you update the database with the command doctrine:schema:update --force
I appreciate any help.
Thank you.
Related
I have a strange problem with Symfony2. I'm working on a project with 40-50 entities, and with one of them this code doesn't work:
$user = $em->getRepository('CompanyUserBundle:User')
->findOneBy([
'username' => $person->getUsername()
]);
I get the error Unknown Entity namespace alias 'CompanyUserBundle'.. The strange thing is that if I change my code to:
$user = $em->getRepository('Company\UserBundle\Entity\User')
->findOneBy([
'username' => $person->getUsername()
]);
It works perfectly... So, what's wrong here? I'm using the getRepository('Alias:Entity') construction all around the code, and there isn't any other problem...
I don't know if it's important, but User is a child entity from FOSUserBundle BaseUser class.
EDIT
Here is my User class (it's located at src/Company/UserBundle/Entity):
namespace Company\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="fos_user")
* #ORM\Entity
*/
class User extends BaseUser
{
/**
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
And here a part of my AppKernel.php:
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
/* ... other, non interesting, bundles... */
new Company\UserBundle\UserBundle(),
new FOS\UserBundle\FOSUserBundle(),
new FR3D\LdapBundle\FR3DLdapBundle(),
new JMS\AopBundle\JMSAopBundle(),
new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
new JMS\DiExtraBundle\JMSDiExtraBundle($this),
);
}
}
More information: as #herr said in the comments, using
$user = $em->getRepository('UserBundle:User')
->findOneBy([
'username' => $person->getUsername()
]);
works fine... But I don't understand why. Why in this bundle the namespace alias is UserBundle instead of CompanyUserBundle? I know that this must be a really silly error, but I can't see it...
You can check in your UserBundle's Bundle class file it will with UserBundle.php instead of CompanyUserBundle.php.
Just try using
$em->getRepository('UserBundle:User')
instead
$em->getRepository('CompanyUserBundle:User')
I think it's namespace / BundleClassName may be only UserBundle not CompanyUserBundle in this case.
Thanks.
I got the same error but the reason was different, because Entity's were in different bundle/package.
The problem caused by the mapping misconfiguration. Changing alias to the correct one solved the issue
doctrine:
dbal:
# ...
orm:
# ...
mappings:
App:
# ...
prefix: 'Company\UserBundle\Entity'
alias: CompanyUserBundle # <-- this alias is used when you do CompanyUserBundle:User
for more information: https://symfony.com/doc/current/reference/configuration/doctrine.html
I must implement and integrate a SAML2 Identity Provider (IdP) with an existing Symfony 2 application.
I found some bundle that implement Service Provider (SP) but not Identity Provider so I think that I may use SimpleSAMLphp library. Are there other solutions?
How can I integrate my user provider logic with SimpleSAMLphp?
UPDATE
As Milos Tomic mentioned in his comment, aerialship/lightsaml is replaced by lightsaml/sp-bundle. You can find an introduction here.
+++++++++++++++++++++++++++
I have recently set up a SAML Solution using Simplesamlphp as IDP and SamlSPBundle as SP and everything is working well.
I recommend installing Simplesamlphp first, following this good Documentation here.
Once you have the IDP up and running, you should see a Welcome page and a Tab called Federation (or something like that, my Installation is in german). There you should see one option "SAML 2.0 IdP Metadata". Follow that link and copy the XML shown to a seperate file and save that file.
On the symfony side, I created a new Bundle and called that "SamlBundle". Download and install SamlSPBundle as described in their Documentation (Step 1 and Step 2).
Create your SSO State/User class (Step 3). Here is an example how I did it:
namespace SamlBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity
* #ORM\Table(name="samlUser")
*/
class SamlUser extends \AerialShip\SamlSPBundle\Entity\SSOStateEntity implements UserInterface
{
/**
* initialize User object and generates salt for password
*/
public function __construct()
{
if (!$this->userData instanceof UserData) {
$this->userData = new UserData();
}
$this->setRoles('ROLE_USER');
}
/**
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string username
*
* #ORM\Column(type="string", length=64, nullable=true)
*/
protected $username;
/**
* #var string targetedId
*
* #ORM\Column(type="string", length=64, nullable=true, name="targeted_id")
*/
protected $targetedID;
/**
* #var string
* #ORM\Column(type="string", length=32, name="provider_id", nullable=true)
*/
protected $providerID;
/**
* #var string
* #ORM\Column(type="string", length=32, name="auth_svc_name")
*/
protected $authenticationServiceName;
/**
* #var string
* #ORM\Column(type="string", length=64, name="session_index", nullable=true)
*/
protected $sessionIndex;
/**
* #var string
* #ORM\Column(type="string", length=64, name="name_id")
*/
protected $nameID;
/**
* #var string
* #ORM\Column(type="string", length=64, name="name_id_format")
*/
protected $nameIDFormat;
/**
* #var \DateTime
* #ORM\Column(type="datetime", name="created_on")
*/
protected $createdOn;
/**
* #var UserData
* #ORM\OneToOne(targetEntity="UserData", cascade={"all"}, fetch="EAGER")
* #ORM\JoinColumn(name="user_data", referencedColumnName="id")
*/
protected $userData;
Add your class to config.yml (Step 4):
# app/config/config.yml
aerial_ship_saml_sp:
driver: orm
sso_state_entity_class: SamlBundle\Entity\SamlUser
Update your security.yml (Step 5). Example;
providers:
saml_user_provider:
id: SamlToState
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
saml:
pattern: ^/(?!login_check)
anonymous: true
aerial_ship_saml_sp:
login_path: /saml/sp/login
check_path: /saml/sp/acs
logout_path: /saml/sp/logout
failure_path: /saml/sp/failure
metadata_path: /saml/sp/FederationMetadata.xml
discovery_path: /saml/sp/discovery
local_logout_path: /logout
provider: saml_user_provider
create_user_if_not_exists: true
services:
openidp:
idp:
#the XML-File you saved from the IDP earlier
file: "#SamlBundle/Resources/idp-FederationMetadata.xml"
sp:
config:
# required, has to match entity id from IDP XML
entity_id: http://your-idp-domain.com
# if different then url being used in request
# used for construction of assertion consumer and logout urls in SP entity descriptor
base_url: http://your-sp-domain.com
signing:
#self signed certificate, see [SamlSPBundle docs][4]
cert_file: "#SamlBundle/Resources/saml.crt"
key_file: "#SamlBundle/Resources/saml.pem"
key_pass: ""
meta:
# must implement SpMetaProviderInterface
# id: my.sp.provider.service.id
# or use builtin SpMetaConfigProvider
# any valid saml name id format or shortcuts: persistent or transient
name_id_format: transient
binding:
# any saml binding or shortcuts: post or redirect
authn_request: redirect
logout_request: redirect
logout:
path: /logout
target: /
invalidate_session: true
Next import the routes as described in Step 6. Before you go on to Step 7, I recommend to create your User provider class first. Here is an example:
namespace SamlBundle\Models;
use SamlBundle\Entity\SamlUser;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use AerialShip\SamlSPBundle\Bridge\SamlSpInfo;
use AerialShip\SamlSPBundle\Security\Core\User\UserManagerInterface as UserManagerInterface;
class SamlToState implements UserManagerInterface
{
/**
* #var ContainerInterface base bundle container
*/
public $container;
/**
* Constructor with DependencyInjection params.
*
* #param \Symfony\Component\DependencyInjection\ContainerInterface $container
*/
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
/**
* {#inheritdoc}
*/
public function loadUserBySamlInfo(SamlSpInfo $samlInfo)
{
$user = $this->loadUserByTargetedID($samlInfo->getAttributes()['eduPersonTargetedID']->getFirstValue());
return $user;
}
private function loadUserByTargetedID($targetedID) {
$repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
$user = $repository->findOneBy(
array('targetedID' => $targetedID)
);
if ($user) {
return $user;
}
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
}
/**
* {#inheritdoc}
*/
public function createUserFromSamlInfo(SamlSpInfo $samlInfo)
{
$repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
$user = $repository->findOneBy(
array('nameID' => $samlInfo->getNameID()->getValue())
);
if ($user) {
$user->setUsername($samlInfo->getAttributes()['eduPersonPrincipalName']->getFirstValue());
$user->setTargetedID($samlInfo->getAttributes()['eduPersonTargetedID']->getFirstValue());
$user->setRoles($samlInfo->getAttributes()['role']->getFirstValue());
} else {
$user = new SamlUser();
$user->setUsername($samlInfo->getAttributes()['eduPersonPrincipalName']->getFirstValue());
$user->setTargetedID($samlInfo->getAttributes()['eduPersonTargetedID']->getFirstValue());
$user->setRoles($samlInfo->getAttributes()['role']->getFirstValue());
$user->setSessionIndex($samlInfo->getAuthnStatement()->getSessionIndex());
$user->setProviderID($samlInfo->getNameID()->getSPProvidedID());
$user->setAuthenticationServiceName($samlInfo->getAuthenticationServiceID());
$user->setNameID($samlInfo->getNameID()->getValue());
$user->setNameIDFormat($samlInfo->getNameID()->getFormat());
}
$em = $this->container->get('doctrine')->getManager();
$em->persist($user);
$em->flush();
return $user;
}
public function loadUserByUsername($username)
{
$repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
$user = $repository->findOneBy(
array('username' => $username)
);
if ($user) {
return $user;
}
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
return false;
}
/**
* {#inheritdoc}
*/
public function refreshUser(UserInterface $user)
{
$repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
$newUser = $repository->findOneBy(
array('nameID' => $user->getNameID())
);
if (!$newUser) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
}
return $newUser;
}
/**
* {#inheritdoc}
*/
public function supportsClass($class)
{
return true;
}
}
Create your service in SamlBundle/Resources/config/services.yml:
services:
SamlToState:
class: SamlBundle\Models\SamlToState
arguments: [#service_container]
Now its time for Step 7, exchanging Metadata. get the SP XML as described and go back to your IDP. You find a "XML to simpleSAMLphp Metadata converter" Link on the Federation Tab. Follow that link and convert your SP XML Data to the simpleSAMLphp format. Add that data to the saml20-sp-remote.php file in your IDPs metadata folder.
Ok, I'm pretty sure that I forgot something, but hopefully this information helps. If you get stuck, your welcome to get back to me.
I found:
SURFnet SamlBundle that is a simple symfony2 wrapper of the library you found.
SamlSpBundle more used and well documented.
Take a look at this two. The first is very simple and i don't know if is sufficient documented, of course is an active project. The second seem more powerful and documented but can be more difficult to configure.
Hope this help
I get this error when I'm persisting my entity
Another "The class 'X' was not found in the chain configured namespaces
This used to work before I moved my Symfony from windows to Linux.
my controller:
public function SubscriptionHandlingAction(Request $request)
{
if ($request->isMethod('POST'))
{
$form = $this->createForm(new NewCustomer(), new Customer());
$form->bind($request);
if ($form->isValid())
{
// get the form data
$newcustomer = $form->getData();
//get the date and set it in the entity
$datecreation = new \DateTime(date('d-m-Y'));
$newcustomer->setdatecreation($datecreation);
//this works fine
echo $newcustomer->getname();
//persist the data
$em = $this->getDoctrine()->getManager();
$em->persist($newcustomer);
$em->flush();
return $this->render('NRtworksSubscriptionBundle:Subscription:subscription_success.html.twig');
}
Of course, my class entity exists, as I can create form based on it, objects etc.
However, this entity is not "mapped" meaning doctrine:mapping:info doesn't give me anything (but I've created manually the corresponding sdl table and put all the annotations):
<?php
namespace NRtworks\SubscriptionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="Customer")
*/
class Customer
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idCustomer;
/**
* #ORM\Column(type="string", length=100, unique = true)
*/
protected $name;
/**
* #ORM\Column(type="string", length=50)
*/
protected $country;
/**
* #ORM\Column(type="datetime", nullable = false)
*/
protected $datecreation;
/**
* #ORM\Column(type="integer", length = 5, nullable = false)
*/
protected $admin_user;
//getter
// no need for that
// setter
// no need for that
}
?>
Any hint(s) of the issue ?
Big thanks
Are you working with multiple entity managers or connections? Make sure each em matches with the corresponding bundles in config.yml under
doctrine:
dbal:
#connection info (driver/host/port/...)
orm:
entity_managers:
manager_one:
connection: # your connection (eg: 'default:'
mappings:
YourRespectiveBundle: ~
AnotherrespectiveBundle: ~
This tripped me up the first time I used multiple ems.
Otherwise check AppKernel.php for your bundle, and double check the db connection is correct.
In Symfony2 you can work with multiple entity managers and use something like the code below:
$em = $this->get('doctrine')->getManager();
$em = $this->get('doctrine')->getManager('default');
$customerEm = $this->get('doctrine')->getManager('customer');
We can inject the default manager to any service by using:
"#doctrine.orm.entity_manager"
How can you inject non-default entity managers into services?
If your entity managers config name is non_default then you can reference it as #doctrine.orm.non_default_entity_manager
For those who are using Symfony 3+, use the console :
php bin/console debug:container
Then you should see many lines starting with : 'doctrine.orm.MY_CUSTOM_ENTITY_MANAGER_xxxxxxxxxx'
So if you want the entity manager corresponding to your custom entity manager, find the line :
'doctrine.orm.MY_CUSTOM_ENTITY_MANAGER_entity_manager'
You can insert it in your service arguments.
Hope it helps.
You should define your custom entity manager as a service:
services:
name_of_your_custom_manager:
class: %doctrine.orm.entity_manager.class%
factory_service: doctrine
factory_method: getEntityManager
arguments: ["name_of_your_custom_manager"]
Then, you can inject it in the same way as you do with every service:
#name_of_your_custom_manager
Edit:
Pay attention that factory method may differ between symfony's version (it could be getEntityManager or getManager)
Hello first of all create your manager, in my example I create the manager for my Item class that is in a CoreBundle:
<?php
// src/Sybio/Bundle/CoreBundle/Manager/ItemManager.php:
namespace Sybio\Bundle\CoreBundle\Manager;
use Sybio\Bundle\CoreBundle\Entity\Item;
class ItemManager
{
/**
* #var \Doctrine\ORM\EntityManager $em entity manager
*/
protected $em;
/**
* #var \Doctrine\ORM\EntityRepository $em repository
*/
protected $repository;
/**
* #var string $entityName
*/
protected $entityName;
/**
* Constructor
*
* #param EntityManager $em
* #param string $entityName
*
* #return void
*/
public function __construct(EntityManager $em, $entityName)
{
$this->em = $em;
$this->repository = $em->getRepository($entityName);
$this->entityName = $entityName;
}
/**
* Save a entity object
*
* #param Object $entity
*
* #return Object Entity
*/
public function save($entity)
{
$this->persistAndFlush($entity);
return $entity;
}
/**
* Remove a entity object
*
* #param Object $entity
*
* #return Object Entity
*/
public function remove($entity)
{
return $this->removeAndFlush($entity);
}
/**
* Persist object
*
* #param mixed $entity
*
* #return void
*/
protected function persistAndFlush($entity)
{
$this->em->persist($entity);
$this->em->flush();
}
/**
* Remove object
*
* #param mixed $entity entity to remove
*
* #return void
*/
protected function removeAndFlush($entity)
{
$this->em->remove($entity);
$this->em->flush();
}
/**
* Returns entity repository object
*
* #return EntityRepository
*/
public function getRepository()
{
return $this->repository;
}
/**
* Create a new object
*
* #return mixed
*/
public function createNewObject()
{
return new Item();
}
// Create your own methods to manage the object
}
If the manager structure is shared between multiple manager, you can create a BaseManager extended by all other managers !
Then register it in the services.yml (or xml) file of your bundle:
# src/Sybio/Bundle/CoreBundle/Resources/config/services.yml or xml !:
parameters:
# Managers _________________
sybio.item_manager.entity: SybioCoreBundle:Item
sybio.item_manager.class: Sybio\Bundle\CoreBundle\Manager\ItemManager
services:
# Managers _________________
sybio.item_manager:
class: %sybio.item_manager.class%
arguments: [#doctrine.orm.entity_manager, %sybio.item_manager.entity%]
That's it, you can now use it:
// Controller:
$im = $this->get('sybio.item_manager');
$item = $im->createNewObject();
$im->save($item);
You can then improve your manager, here I give an array of config parameters to my manager:
# src/Sybio/Bundle/CoreBundle/Resources/config/services.yml or xml !:
sybio.item_manager:
class: %sybio.item_manager.class%
arguments: [#doctrine.orm.entity_manager, %sybio.item_manager.entity%, {'item_removed_state': %item_removed_state%, 'item_unpublished_state': %item_unpublished_state%, 'item_published_state': %item_published_state%}]
// src/Sybio/Bundle/CoreBundle/Manager/ItemManager.php:
public function __construct(EntityManager $em, $entityName, $params = array()) {
// ...
$this->params = $params;
}
If you create a BaseManager, you can also create a usefull generic method to initialize an object:
// src/Sybio/Bundle/CoreBundle/Manager/BaseManager.php:
/**
* Create a new object
*
* #return mixed
*/
public function createNewObject()
{
$entityName = explode(":", $this->entityName);
$entityName = "Sybio\Bundle\CoreBundle\Entity\\".$entityName[1];
return new $entityName;
}
I use FosUserBundle and SonataUserBundle for my Symfony2 project.
I get confused now. I want to add fields for the entity User but it's not working. There is no update for the schema for example.
Here is my config :
AppKernel:
...
new FOS\UserBundle\FOSUserBundle(),
new Sonata\UserBundle\SonataUserBundle(),
new Application\Sonata\UserBundle\ApplicationSonataUserBundle('FOSUserBundle')
config.yml:
...
# FOSUserBundle Configuration
fos_user:
db_driver: orm # BDD type
firewall_name: main # firewall name
user_class: Application\Sonata\UserBundle\Entity\User # entity class defined
And the User entity with added fields, in app/Application/Sonata/userBundle/Entity/
namespace Application\Sonata\UserBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
/**
* This file has been generated by the Sonata EasyExtends bundle ( http://sonata- project.org/easy-extends )
*
* References :
* working with object : http://www.doctrine-project.org/projects/orm/2.0 /docs/reference/working-with-objects/en
*
* #author <yourname> <youremail>
*/
class User extends BaseUser
{
/**
* #var integer $id
*/
protected $id;
/**
* #var string
*/
protected $institution;
/**
* #var string
*/
protected $department;
/**
* #var string
*/
protected $city;
/**
* #var string
*/
protected $country;
/**
* Get id
*
* #return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getInstitution()
{
return $this->institution;
}
/**
* #return string
*/
public function getDepartment()
{
return $this->department;
}
/**
* #return string
*/
public function getCity()
{
return $this->city;
}
/**
* #return string
*/
public function getCountry()
{
return $this->country;
}
}
In my database, table fos_user_user seems to be the table with user saved data.
Added fields (country, city...) are not created when calling "php app/console doctrine:schema:update --force". How to add fields to the user entity ? I'm lost in fosuserbundle and sonatauserbundle....
In fact, it appear that the way i use fos and sonata user bundles force me to use XML definition. So for example adding a field called "city" you have to add these lines :
User.php (in /app/Application/Sonata/UserBundle/Entity/) :
protected $city;
User.orm.xml (in /app/Application/Sonata/UserBundle/Ressource/config/doctrine/) :
<field name="city" column="city" type="text"/>
Maybe putting this in the documentation of the bundles would be interesting for newbies ;)
I found a better solution if you want use annotations.
Delete UserBundle/Resorces/config/doctrine folder and you can use annotations in your UserBundle/Entity folder