SORU
18 HAZİRAN 2012, PAZARTESİ


Ekleme Varlıkların Symfony2 koleksiyonu - varolan varlıklarla Derneği/Kaldır?

1. Hızlı bir bakış

1.1 Amaç

Ulaşmak istediğim oluştur/Düzenle kullanıcı bir araçtır. Düzenlenebilir alanları:

  • (tip: metin) kullanıcı adı
  • (türü: parola) plainPassword
  • (tip: e-posta) e-posta
  • (tür: toplama grupları
  • (tür: toplama) avoRoles

Not: son adlı bir özellik değildirroller . $ çünkü benim Kullanıcı sınıfı FOSUserBundle Kullanıcı sınıfının genişletilmesi ve rolleri üzerine daha fazla sorun getirdi. Bunları önlemek için sadece roller koleksiyonumu altında saklamak için karar verdi$avoRoles.

1.2 Kullanıcı Arayüzü

My template 2 bölümden oluşur:

  1. Kullanıcı formu
  2. Masa görüntüleniyor $userRepository->($kullanıcı) findAllRolesExceptOwnedByUser;

Not: () findAllRolesExceptOwnedByUser özel depo işlevini verir tüm rolleri alt (henüz atanan $kullanıcı olmayanlar).

1.3 İstenen işlevsellik

1.3.1 rolü Ekleyin:


    WHEN user clicks " " (add) button in Roles table  
    THEN jquery removes that row from Roles table  
    AND  jquery adds new list item to User form (avoRoles list)

1.3.2 Kaldırmak rolleri:


    WHEN user clicks "x" (remove) button in  User form (avoRoles list)  
    THEN jquery removes that list item from User form (avoRoles list)  
    AND  jquery adds new row to Roles table

1.3.3 değişiklikleri Kaydet:


    WHEN user clicks "Zapisz" (save) button  
    THEN user form submits all fields (username, password, email, avoRoles, groups)  
    AND  saves avoRoles as an ArrayCollection of Role entities (ManyToMany relation)  
    AND  saves groups as an ArrayCollection of Role entities (ManyToMany relation)  

Not: SADECE varolan Roller ve Gruplar Kullanıcı atanabilir. Eğer herhangi bir nedenle bulundukları takdirde forma doğrulamak gerekir.


2. Kod

Bu bölümde/İ takdim veya kısa bir süre sonra bu eylemin arkasında kod açıklayın. Açıklama yeterli değil, görmeniz gerekiyor eğer kodu sadece bana söyle ve bunu yapıştır. İlk etapta gereksiz kod ile spam önlemek için pasteing değilim.

2.1 Kullanıcı sınıfı

Kullanıcı Dersim FOSUserBundle kullanıcı sınıfı genişletir.

namespace Avocode\UserBundle\Entity;

use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Avocode\CommonBundle\Collections\ArrayCollection;
use Symfony\Component\Validator\ExecutionContext;

/**
 * @ORM\Entity(repositoryClass="Avocode\UserBundle\Repository\UserRepository")
 * @ORM\Table(name="avo_user")
 */
class User extends BaseUser
{
    const ROLE_DEFAULT = 'ROLE_USER';
    const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\generatedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Group")
     * @ORM\JoinTable(name="avo_user_avo_group",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     */
    protected $groups;

    /**
     * @ORM\ManyToMany(targetEntity="Role")
     * @ORM\JoinTable(name="avo_user_avo_role",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
     * )
     */
    protected $avoRoles;

    /**
     * @ORM\Column(type="datetime", name="created_at")
     */
    protected $createdAt;

    /**
     * User class constructor
     */
    public function __construct()
    {
        parent::__construct();

        $this->groups = new ArrayCollection();        
        $this->avoRoles = new ArrayCollection();
        $this->createdAt = new \DateTime();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set user roles
     * 
     * @return User
     */
    public function setAvoRoles($avoRoles)
    {
        $this->getAvoRoles()->clear();

        foreach($avoRoles as $role) {
            $this->addAvoRole($role);
        }

        return $this;
    }

    /**
     * Add avoRole
     *
     * @param Role $avoRole
     * @return User
     */
    public function addAvoRole(Role $avoRole)
    {
        if(!$this->getAvoRoles()->contains($avoRole)) {
          $this->getAvoRoles()->add($avoRole);
        }

        return $this;
    }

    /**
     * Get avoRoles
     *
     * @return ArrayCollection
     */
    public function getAvoRoles()
    {
        return $this->avoRoles;
    }

    /**
     * Set user groups
     * 
     * @return User
     */
    public function setGroups($groups)
    {
        $this->getGroups()->clear();

        foreach($groups as $group) {
            $this->addGroup($group);
        }

        return $this;
    }

    /**
     * Get groups granted to the user.
     *
     * @return Collection
     */
    public function getGroups()
    {
        return $this->groups ?: $this->groups = new ArrayCollection();
    }

    /**
     * Get user creation date
     *
     * @return DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }
}

2.2 Rolü sınıf

Benim rolüm sınıf Symfony Güvenlik Bileşeni Çekirdek Rolü sınıf uzanır.

namespace Avocode\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Avocode\CommonBundle\Collections\ArrayCollection;
use Symfony\Component\Security\Core\Role\Role as BaseRole;

/**
 * @ORM\Entity(repositoryClass="Avocode\UserBundle\Repository\RoleRepository")
 * @ORM\Table(name="avo_role")
 */
class Role extends BaseRole
{    
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\generatedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", unique="TRUE", length=255)
     */
    protected $name;

    /**
     * @ORM\Column(type="string", length=255)
     */
    protected $module;

    /**
     * @ORM\Column(type="text")
     */
    protected $description;

    /**
     * Role class constructor
     */
    public function __construct()
    {
    }

    /**
     * Returns role name.
     * 
     * @return string
     */    
    public function __toString()
    {
        return (string) $this->getName();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Role
     */
    public function setName($name)
    {      
        $name = strtoupper($name);
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set module
     *
     * @param string $module
     * @return Role
     */
    public function setModule($module)
    {
        $this->module = $module;

        return $this;
    }

    /**
     * Get module
     *
     * @return string 
     */
    public function getModule()
    {
        return $this->module;
    }

    /**
     * Set description
     *
     * @param text $description
     * @return Role
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return text 
     */
    public function getDescription()
    {
        return $this->description;
    }
}

2.3 Grupları sınıf

Rolleri gibi gruplarla aynı sorun var bu yana, burada onları es geçiyorum. Ben rolleri çalışma alırsam grupları ile aynı yapabileceğimi biliyorum.

2.4 Kumanda

namespace Avocode\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\SecurityContext;
use JMS\SecurityExtraBundle\Annotation\Secure;
use Avocode\UserBundle\Entity\User;
use Avocode\UserBundle\Form\Type\UserType;

class UserManagementController extends Controller
{
    /**
     * User create
     * @Secure(roles="ROLE_USER_ADMIN")
     */
    public function createAction(Request $request)
    {      
        $em = $this->getDoctrine()->getEntityManager();

        $user = new User();
        $form = $this->createForm(new UserType(array('password' => true)), $user);

        $roles = $em->getRepository('AvocodeUserBundle:User')
                    ->findAllRolesExceptOwned($user);
        $groups = $em->getRepository('AvocodeUserBundle:User')
                    ->findAllGroupsExceptOwned($user);

        if($request->getMethod() == 'POST' && $request->request->has('save')) {
            $form->bindRequest($request);

            if($form->isValid()) {
                /* Persist, flush and redirect */
                $em->persist($user);
                $em->flush();
                $this->setFlash('avocode_user_success', 'user.flash.user_created');
                $url = $this->container->get('router')->generate('avocode_user_show', array('id' => $user->getId()));

                return new RedirectResponse($url);
            }
        }

        return $this->render('AvocodeUserBundle:UserManagement:create.html.twig', array(
          'form' => $form->createView(),
          'user' => $user,
          'roles' => $roles,
          'groups' => $groups,
        ));
    }
}

2.5 Özel havuzları

Gerekli sadece iyi çalışır beri Bu yazı için değil - Grupları/tüm Rolleri (kullanıcı atanmamış) alt dönerler.

2.6 UserType

UserType:

namespace Avocode\UserBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class UserType extends AbstractType
{    
    private $options; 

    public function __construct(array $options = null) 
    { 
        $this->options = $options; 
    }

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('username', 'text');

        // password field should be rendered only for CREATE action
        // the same form type will be used for EDIT action
        // thats why its optional

        if($this->options['password'])
        {
          $builder->add('plainpassword', 'repeated', array(
                        'type' => 'text',
                        'options' => array(
                          'attr' => array(
                            'autocomplete' => 'off'
                          ),
                        ),
                        'first_name' => 'input',
                        'second_name' => 'confirm', 
                        'invalid_message' => 'repeated.invalid.password',
                     ));
        }

        $builder->add('email', 'email', array(
                        'trim' => true,
                     ))

        // collection_list is a custom field type
        // extending collection field type
        //
        // the only change is diffrent form name
        // (and a custom collection_list_widget)
        // 
        // in short: it's a collection field with custom form_theme
        // 
                ->add('groups', 'collection_list', array(
                        'type' => new GroupNameType(),
                        'allow_add' => true,
                        'allow_delete' => true,
                        'by_reference' => true,
                        'error_bubbling' => false,
                        'prototype' => true,
                     ))
                ->add('avoRoles', 'collection_list', array(
                        'type' => new RoleNameType(),
                        'allow_add' => true,
                        'allow_delete' => true,
                        'by_reference' => true,
                        'error_bubbling' => false,
                        'prototype' => true,
                     ));
    }

    public function getName()
    {
        return 'avo_user';
    }

    public function getDefaultOptions(array $options){

        $options = array(
          'data_class' => 'Avocode\UserBundle\Entity\User',
        );

        // adding password validation if password field was rendered

        if($this->options['password'])
          $options['validation_groups'][] = 'password';

        return $options;
    }
}

2.7 RoleNameType

Bu formu işlemek gerekiyordu

  • gizli Rol KİMLİĞİ
  • Adı (salt OKUNUR) rolü
  • modülü (SADECE OKUNUR) gizli
  • açıklama (salt OKUNUR) gizli
  • (x) düğmesini kaldırın

Modül ve açıklama Admin bir Kullanıcı bir rol kaldırır, bu rolü bir WordPress kullanmak için Rolleri Tablo eklenmesi gerekir - ve bu tablo, Modül ve Açıklama sütunları vardır çünkü gizli alanlar olarak işlenmiş.

namespace Avocode\UserBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class RoleNameType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder            
            ->add('', 'button', array(
              'required' => false,
            ))  // custom field type rendering the "x" button

            ->add('id', 'hidden')

            ->add('name', 'label', array(
              'required' => false,
            )) // custom field type rendering <span> item instead of <input> item

            ->add('module', 'hidden', array('read_only' => true))
            ->add('description', 'hidden', array('read_only' => true))
        ;        
    }

    public function getName()
    {
        // no_label is a custom widget that renders field_row without the label

        return 'no_label';
    }

    public function getDefaultOptions(array $options){
        return array('data_class' => 'Avocode\UserBundle\Entity\Role');
    }
}

3. Mevcut/bilinen Sorunlar

3.1 Durum 1: yapılandırma sözleri yukarıda

Yukarıdaki yapılandırma hata verir:

Property "id" is not public in class "Avocode\UserBundle\Entity\Role". Maybe you should create the method "setId()"?

Ama KİMLİĞİ için belirleyici gerekli değil.

  1. YENİ bir rol yaratmak istemiyorum ilk çok doğal. Sadece mevcut Rol ve Kullanıcı kuruluşlar arasında bir ilişki oluşturmak istiyorum.
  2. Eğer yeni bir Rol oluşturmak istiyorsanız Eğer olsaydı bile, bu KİMLİĞİ otomatik oluşturulan olmalıdır:

    /**

    • @\Id ORM
    • @ORM\Sütun(tür="") tamsayı
    • @ORM\generatedValue(strategy="") OTOMATİK */ korumalı $id;

3.2 Durum 2: Rolü varlık KİMLİĞİ özelliği eklendi pasör

Yanlış olduğunu düşünüyorum, ama sadece emin olmak için yaptım. Rolü varlık için bu kodu ekledikten sonra:

public function setId($id)
{
    $this->id = $id;
    return $this;
}

Yeni kullanıcı oluşturmak ve bir rol ekleyin, KAYDEDİN... olur:

  1. Yeni kullanıcı oluşturulur
  2. Yeni kullanıcı istenen KİMLİĞİ atanmış (yay!) rolü vardır
  3. ancak bu rolün adı boş bir dize yazılır(kahretsin!)

Açıkçası, istediğim şey bu değil. /Üzerine rolleri düzenlemek istemiyorum. Ben sadece onları ve Kullanıcı arasında bir ilişki eklemek istiyorum.

3.3 Durum 3: geçici Çözüm, Jeppe tarafından önerilen

Ben ilk bu sorunla karşılaştı zaman bir çözüm ile sona erdi, aynı bu Jeppe önerdi. Bugün (başka nedenlerden dolayı) ben benim form/view yeniden yapmak zorunda kaldı ve geçici çalışmayı durdurdu.

Ne gibi değişiklikler Case3 içinde UserManagementController ->createAction:

  // in createAction
  // instead of $user = new User
  $user = $this->updateUser($request, new User());

  //and below updateUser function


    /**
     * Tworzy nowego użytkownika i nadaje mu właściwości
     * na podstawie przesłanego żądania
     * 
     * @return User Zwraca skonfigurowanego użytkownika
     */
    protected function updateUser($request, $user)
    {
        if($request->getMethod() == 'POST')
        {
          $avo_user = $request->request->get('avo_user');

          /**
           * Ustawiam i dodaję/odejmuję właściwe grupy dla użytkownika
           */
          $owned_groups = (array_key_exists('groups', $avo_user)) ? $avo_user['groups'] : array();
          foreach($owned_groups as $key => $group) {
            $owned_groups[$key] = $group['id'];
          }

          if(count($owned_groups) > 0)
          {
            $em = $this->getDoctrine()->getEntityManager();
            $groups = $em->getRepository('AvocodeUserBundle:Group')->findById($owned_groups);
            $user->setGroups($groups);
          }

          /**
           * Ustawiam i dodaję/odejmuję właściwe role dla użytkownika
           */
          $owned_roles = (array_key_exists('avoRoles', $avo_user)) ? $avo_user['avoRoles'] : array();
          foreach($owned_roles as $key => $role) {
            $owned_roles[$key] = $role['id'];
          }

          if(count($owned_roles) > 0)
          {
            $em = $this->getDoctrine()->getEntityManager();
            $roles = $em->getRepository('AvocodeUserBundle:Role')->findById($owned_roles);
            $user->setAvoRoles($roles);
          }

          /**
           * Ustawiam pozostałe właściwości
           */
          $user->setUsername($avo_user['username']);
          $user->setEmail($avo_user['email']);

          if($request->request->has('generate_password'))
            $user->setPlainPassword($user->generateRandomPassword());  
        }

        return $user;
    }

Ne yazık ki bu sonuçları da DURUM1 (hiçbir KİMLİK belirleyici ile) veya CASE2 (ID ayarlayıcı ile).. bir şeyi değiştirmez.

Kullanıcı dostu . tarafından önerilen 3.4 Durum 4:

Cascade=ekleme {"", ""} Kaldır eşleme. devam

/**
 * @ORM\ManyToMany(targetEntity="Group", cascade={"persist", "remove"})
 * @ORM\JoinTable(name="avo_user_avo_group",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
 * )
 */
protected $groups;

/**
 * @ORM\ManyToMany(targetEntity="Role", cascade={"persist", "remove"})
 * @ORM\JoinTable(name="avo_user_avo_role",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
 * )
 */
protected $avoRoles;

Ve changeingby_referenceiçinyanlışFormType:

// ...

                ->add('avoRoles', 'collection_list', array(
                        'type' => new RoleNameType(),
                        'allow_add' => true,
                        'allow_delete' => true,
                        'by_reference' => false,
                        'error_bubbling' => false,
                        'prototype' => true,
                     ));

// ...

Ve geçici çözüm kodu 3.3 önerilen tutarak bir şeyler değişti:

  1. Kullanıcı ve rol arasındaki ilişkioluşturulan değil
  2. ama Rolü varlık .. adı boş bir dize (3.2) üzerine

Yani.. ama yanlış yönde bir şeyler değişti.

4. Sürümleri

4.1 Symfony2 v2.0.15

4.2 Doctrine2 v2.1.7

4.3 FOSUserBundle sürümü: 6fb81861d84d460f1d070ceb8ec180aac841f7fa

5. Özet

Birçok farklı yaklaşımlar denedim en son sadece onlar (yukarıda) ve saat kodları inceliyor, bence google ve cevap aramaya harcanan sonra bu çalışma alamadım.

Herhangi bir yardım büyük takdir edilecektir. Eğer bilmek istediğin bir şey olursa ne gerekiyorsa göndeririz.

CEVAP
10 EKİM 2012, ÇARŞAMBA


Form bileşeni ile yanlış bir şey var, o da aynı sonuca vardım ve bunu düzeltmek için kolay bir yol göremiyorum. Ancak, ben bir biraz daha az hantal geçici çözüm bu tamamen genel; yok kodlanmış bilgi varlıkları/niteliklerini çözecektir herhangi bir koleksiyon karşısına çıkıyor:

Basit, genel bir çözüm yöntemi

Bu varlık herhangi bir değişiklik gerektirmez.

use Doctrine\Common\Collections\Collection;
use Symfony\Component\Form\Form;

# In your controller. Or possibly defined within a service if used in many controllers

/**
 * Ensure that any removed items collections actually get removed
 *
 * @param \Symfony\Component\Form\Form $form
 */
protected function cleanupCollections(Form $form)
{
    $children = $form->getChildren();

    foreach ($children as $childForm) {
        $data = $childForm->getData();
        if ($data instanceof Collection) {

            // Get the child form objects and compare the data of each child against the object's current collection
            $proxies = $childForm->getChildren();
            foreach ($proxies as $proxy) {
                $entity = $proxy->getData();
                if (!$data->contains($entity)) {

                    // Entity has been removed from the collection
                    // DELETE THE ENTITY HERE

                    // e.g. doctrine:
                    // $em = $this->getDoctrine()->getEntityManager();
                    // $em->remove($entity);

                }
            }
        }
    }
}

Yeni aramacleanupCollections()önce kalıcı yöntem

# in your controller action...

if($request->getMethod() == 'POST') {
    $form->bindRequest($request);
    if($form->isValid()) {

        // 'Clean' all collections within the form before persisting
        $this->cleanupCollections($form);

        $em->persist($user);
        $em->flush();

        // further actions. return response...
    }
}

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • CareyHolzman

    CareyHolzman

    24 Ocak 2008
  • Caroline Saquet

    Caroline Saq

    1 EKİM 2011
  • michellefeng's channel

    michellefeng

    26 Kasım 2006