******PHP Fatal error Namespace declaration statement has to be the very first statement in the script in C:\xampp\htdocs\yii2\advanced\frontend\models\Employee.php on line 2******
What code showing code below is my code while running am getting above mentioned error
<?php
namespace frontend\models;
use Yii;
/**
* This is the model class for table "employee".
*
* #property integer $Emp_id
* #property string $Emp_Name
* #property string $Emp_Gender
* #property string $Emp_Designation
* #property double $Emp_Sallary
*
* #property Empinfo[] $empinfos
*/
class Employee extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'employee';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['Emp_id', 'Emp_Name', 'Emp_Gender', 'Emp_Designation', 'Emp_Sallary'], 'required'],
[['Emp_id'], 'integer'],
[['Emp_Sallary'], 'number'],
[['Emp_Name', 'Emp_Gender', 'Emp_Designation'], 'string', 'max' => 45]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'Emp_id' => 'Emp ID',
'Emp_Name' => 'Emp Name',
'Emp_Gender' => 'Emp Gender',
'Emp_Designation' => 'Emp Designation',
'Emp_Sallary' => 'Emp Sallary',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getEmpinfos()
{
return $this->hasMany(Empinfo::className(), ['EmpId' => 'Emp_id']);
}
}
Your Empinfo::className() is not namespaced
May be something like
use app/models/Empinfo;
use Yii;
function rules(){}
......
Related
I need to reset password user with api plateform. I have create custom endpoint :
itemOperations: [
'sendEmail' => [
'method' => 'patch',
'path' => '/users/password/send/token',
'controller' => ResetPasswordRequestsController::class,
'normalization_context' => ['groups' => [
'read:sendEmail:user:item',
]],
],
]
In my controller search user with email and create token for reset passeword.
When i send my request i got a error : "Invalid identifier value or configuration." :
PATH : http://192.168.0.11:8000/api/users/password/send/token
{
"email": "user#email.com"
}
I think Api plateform need a id of my entity User with PATCH method but i send only email.
User Entty attributs
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #Assert\NotBlank(groups={"sendEmail"})
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
* #Assert\NotBlank
*/
private $password;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $firstName;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $lastName;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\OneToOne(targetEntity=Teacher::class, mappedBy="user", cascade={"persist", "remove"})
*/
private $teacher;
/**
* #ORM\OneToOne(targetEntity=Student::class, mappedBy="user", cascade={"persist", "remove"})
*/
private $student;
/**
* #ORM\ManyToMany(targetEntity=Role::class, mappedBy="users")
*/
private $dbroles;
/**
* #ORM\OneToMany(targetEntity=Invitation::class, mappedBy="owner")
*/
private $invitations;
/**
* #ORM\OneToMany(targetEntity=Inscription::class, mappedBy="userRegister",cascade={"persist"})
*/
private $inscriptions;
/**
* #ORM\OneToMany(targetEntity=UserInvitation::class, mappedBy="owner")
*/
private $userInvitations;
/**
* #ORM\OneToMany(targetEntity=UserInvitation::class, mappedBy="guest")
*/
private $userInvitationsGuest;
}
my controller :
<?php
namespace App\Controller;
use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ResetPasswordRequestsController extends AbstractController
{
use ResetPasswordControllerTrait;
private $mailer;
private $resetPasswordHelper;
private $passwordEncoder;
public function __construct(ValidatorInterface $validator, MailerInterface $mailer, ResetPasswordHelperInterface $resetPasswordHelper, UserPasswordEncoderInterface $passwordEncoder)
{
$this->mailer = $mailer;
$this->resetPasswordHelper = $resetPasswordHelper;
$this->passwordEncoder = $passwordEncoder;
$this->validator = $validator;
}
public function __invoke(Request $request)
{
$data = $request->get('data');
if($data->getEmail()){
$this->sendEmail($data->getEmail());
}
if($data->getPassword() && $request->get("token")){
$this->resetPassword($data->getPassword(), $request->get("token"));
}
}
public function resetPassword($newPassword, $token){
try {
$user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
} catch (ResetPasswordExceptionInterface $e) {
$this->addFlash('reset_password_error', sprintf(
'There was a problem validating your reset request - %s',
$e->getReason()
));
return $this->redirectToRoute('app_forgot_password_request');
}
$this->resetPasswordHelper->removeResetRequest($token);
$encodedPassword = $this->passwordEncoder->encodePassword(
$user,
$newPassword
);
$user->setPassword($encodedPassword);
$this->getDoctrine()->getManager()->flush();
// The session is cleaned up after the password has been changed.
$this->cleanSessionAfterReset();
return $user;
}
public function sendEmail($email){
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy([
'email' => $email,
]);
if (!$user) {
return throw new Exception(sprintf("L'utilisateur est introuvable"));
}
try {
$resetToken = $this->resetPasswordHelper->generateResetToken($user);
} catch (ResetPasswordExceptionInterface $e) {
return throw new Exception(sprintf("Erreur lors de la génération du token"));
}
$token = $resetToken->getToken();
$url = $this->getParameter('front_app_url');
$redirectUrl = $url . "/auth/reset/" . $token;
$email = (new TemplatedEmail())
->from(new Address('mouchelet.thomas#gmail.com', 'thomas mouchelet'))
->to($user->getEmail())
->subject('Your password reset request')
->htmlTemplate('reset_password/email.html.twig')
->context([
'resetToken' => $resetToken,
'redirectUrl' => $redirectUrl
])
;
$this->mailer->send($email);
$this->setTokenObjectInSession($resetToken);
return $user;
}
}
EDIT 1 : Not working
thx i have try it
collectionOperations: [
'sendEmail' => [
'write' => false,
'serialize' => false,
'method' => 'post',
'path' => '/users/password/send/token',
'controller' => ResetPasswordRequestsController::class,
],
],
i have try with validations_groups :
collectionOperations: [
'post' => ['validation_groups' => ['Default', 'post:user']],
]
#Assert\NotBlank(groups={"post:user"})
but i have error :
Cannot validate values of type \"null\" automatically. Please provide a constraint.
By default, GET, PUT and PATCH methods needs an identifier.
There is several way to achieve what you want.
The first one is to fallback to a regular Symfony controller. Just declare a new POST endpoint instead of PATCH. The counterpart is that this endpoint won't be visible in the OpenAPI view.
The second one is to switch to a POST operation. POST operation does not need an identifier. Because you don't want to save anything, you could either return a Response from your controller to stop the lifecycle, or disable the WriteListener (and perhaps the SerializeListener as well) :
itemOperations: [
'sendEmail' => [
'write' => false,
'serialize' => false,
'method' => 'post',
'path' => '/users/password/send/token',
'controller' => ResetPasswordRequestsController::class,
'normalization_context' => ['groups' => [
'read:sendEmail:user:item',
]],
],
]
The third one looks like the second, keep your PATCH operation, but disable the ReadListener: 'read' => false. The ReadListener is the part which require an identifier. I guess your controller will receive an array containing only the body of your request.
I have problem with query builder using Doctrine 2 in Symfony 4.4 and Omines/Datatables Bundle.
I have two entities, User and Log, which look like this:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
*/
class User
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=50)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=50)
*/
private $lastname;
/**
* #ORM\OneToMany(targetEntity=Log::class, mappedBy="user")
*/
private $logs;
public function __construct()
{
$this->logs = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getLastname(): ?string
{
return $this->lastname;
}
public function setLastname(string $lastname): self
{
$this->lastname = $lastname;
return $this;
}
/**
* #return Collection|Log[]
*/
public function getLogs(): Collection
{
return $this->logs;
}
public function addLog(Log $log): self
{
if (!$this->logs->contains($log)) {
$this->logs[] = $log;
$log->setUser($this);
}
return $this;
}
public function removeLog(Log $log): self
{
if ($this->logs->contains($log)) {
$this->logs->removeElement($log);
// set the owning side to null (unless already changed)
if ($log->getUser() === $this) {
$log->setUser(null);
}
}
return $this;
}
}
Entity Log:
<?php
namespace App\Entity;
use App\Repository\LogRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=LogRepository::class)
*/
class Log
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $logStart;
/**
* #ORM\Column(type="string", length=15)
*/
private $ip;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="logs")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getLogStart(): ?\DateTimeInterface
{
return $this->logStart;
}
public function setLogStart(\DateTimeInterface $logStart): self
{
$this->logStart = $logStart;
return $this;
}
public function getIp(): ?string
{
return $this->ip;
}
public function setIp(string $ip): self
{
$this->ip = $ip;
return $this;
}
public function getUser(): ?user
{
return $this->user;
}
public function setUser(?user $user): self
{
$this->user = $user;
return $this;
}
}
I also use the omines/datatables bundle (Documentation and link to github)
I tried to build a query with a left join to my User entity. My code in the controller is as following:
$table = $this->datatableFactory->create([])
->add('id', TextColumn::class, ['label' => '#', 'className' => 'bold', 'searchable' => true])
->add('firstname lastname', TextColumn::class, ['label' => $translator->trans('Customer name'), 'className' => 'bold', 'searchable' => true])
->add('logStart', DateTimeColumn::class, ['label' => $translator->trans('Time'), 'className' => 'bold', 'searchable' => false])
->createAdapter(ORMAdapter::class, [
'entity' => Log::class,
'query' => function (QueryBuilder $queryBuilder) {
$queryBuilder
->select('l, u.firstname, u.lastname')
->from(Log::class, 'l')
->leftJoin(User::class, 'u', Join::ON, 'l.user = u');
}
]);
$table->handleRequest($request);
if ($table->isCallback()) {
return $table->getResponse();
}
And I got the following error: Syntax Error line 0, col 60: Error: Expected end of string, got 'ON'
But when I changed the following: ->leftJoin(User::class, 'u', Join::ON, 'l.user = u');
to: ->leftJoin(User::class, 'u', Join::WITH, 'l.user = u.id'); I get the following error:
Cannot read property "id" from an array. Maybe you intended to write
the property path as "[id]" instead.
Does anyone have an idea what I'm doing wrong?
Thank you for every help :)
EDIT:
I found a solution on github and I changed my code to:
->createAdapter(ORMAdapter::class, [
'hydrate' => \Doctrine\ORM\Query::HYDRATE_ARRAY,
'entity' => Log::class,
'query' => function (QueryBuilder $queryBuilder) {
$queryBuilder
->select('l, u')
->from(Log::class, 'l')
->leftJoin('l.user', 'u');
}
]);
but this didn't change anything for me. I still don't have access to the User entity (in this case for example firstname and lastname).
Problem solved. In many to one relation we must use 'field' option in column. For example:
$table = $dataTableFactory->create()
->add('firstName', TextColumn::class, ['label' => 'Firstname', 'field' => 'user.firstname'])
->add('logStart', DateTimeColumn::class, ['label' => 'Log start'])
->createAdapter(ORMAdapter::class, [
'entity' => Log::class,
])
->handleRequest($request);
Im trying to upload a file using SonataAdminBundle.
I don't know what am I doing wrong as I followed instructions from official documentation https://sonata-project.org/bundles/admin/3-x/doc/cookbook/recipe_file_uploads.html
The upload() function is not triggering, and even when I invoke the method in DocumentAdmin it does not put the files in the directory I specified.
It seems like the yaml file is not even read, but how am I supposed to configure it ?
prePersist() and preUpdate() are triggered even without it.
Code:
final class DocumentAdmin extends AbstractAdmin
{
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('title', null, [
'label' => 'Name'
])
->add('documentCategory', null, [
'label' => 'Typ'
])
->add('priority', null, [
'label' => 'Order'
])
;
}
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('file', FileType::class, [
'required' => true,
])
->add('title', null, [
'label' => 'Name'
])
->add('priority', null, [
'label' => 'Priority'
])
->add('documentCategory', null, [
'label' => 'Typ'
])
;
}
public function prePersist($document)
{
$this->manageFileUpload($document);
}
public function preUpdate($document)
{
$this->manageFileUpload($document);
}
private function manageFileUpload($document)
{
if ($document->getFile()) {
$document->refreshUpdated();
}
}
public function toString($object)
{
return $object instanceof Document
? $object->getTitle()
: 'File'; // shown in the breadcrumb on the create view
}
}
Document Entity
class Document
{
const SERVER_PATH_TO_DOCUMENTS_FOLDER = '%kernel.project_dir%/public/uploads';
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* Unmapped property to handle file uploads
*/
private $file;
/**
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Manages the copying of the file to the relevant place on the server
*/
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// we use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and target filename as params
$file = $this->getFile();
$directory = self::SERVER_PATH_TO_DOCUMENTS_FOLDER;
$originaName = $this->getFile()->getClientOriginalName();
// dump($file);
// dump($directory);
// dump($originaName);
// die();
$file->move($directory, $originaName);
// set the path property to the filename where you've saved the file
// clean up the file property as you won't need it anymore
$this->setFile(null);
}
/**
* Lifecycle callback to upload the file to the server.
*/
public function lifecycleFileUpload()
{
$this->upload();
}
/**
* Updates the hash value to force the preUpdate and postUpdate events to fire.
*/
public function refreshUpdated()
{
$this->setDateOfUpload(new \DateTime());
}
/**
* #ORM\Column
* #Gedmo\UploadableFileName
*/
private $title;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
private $dateOfUpload;
/**
* #ORM\Column(type="smallint")
*/
private $priority;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\DocumentCategory", inversedBy="documents")
* #ORM\JoinColumn(nullable=false)
*/
private $documentCategory;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
/**
* #return \DateTime
*/
public function getDateOfUpload()
{
return $this->dateOfUpload;
}
public function setDateOfUpload(\DateTimeInterface $dateOfUpload): self
{
$this->dateOfUpload = new \DateTime();
return $this;
}
public function getPriority(): ?int
{
return $this->priority;
}
public function setPriority(int $priority): self
{
$this->priority = $priority;
return $this;
}
public function getDocumentCategory(): ?DocumentCategory
{
return $this->documentCategory;
}
public function setDocumentCategory(?DocumentCategory $documentCategory): self
{
$this->documentCategory = $documentCategory;
return $this;
}
// public function myCallbackMethod(array $info)
// {
// }
public function __toString()
{
return $this->title;
}
}
EDIT: I changed the file directory path to:
const SERVER_PATH_TO_DOCUMENTS_FOLDER = 'uploads/documents';
And also invoked the lifecycleFileUpload() method in manageFileUpload() method
and now the files are moved to the directory.
Is it the right way to upload ?
I'm trying to get a smooth admin interface similar as if when two entities are associated by a many-to-many relationship. I need the join table to define additional information like rank. I don't want to display the jointable entity on the backend, it should be writeable in the edit-menu of at least one entity. My example:
class Question{
/**
* #Assert\NotBlank()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Options2Questions", mappedBy="question", cascade={"persist"})
*/
private $optionQuestion;
public function __construct() {
$this->optionQuestion = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion){
$this->optionQuestion[] = $optionQuestion;
return $this;
}
public function removeOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion){
$this->optionQuestion->removeElement($optionQuestion);
}
public function getOptionQuestion(){
return $this->optionQuestion;
}
}
class Options{
/**
* #Assert\NotBlank()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Options2Questions", mappedBy="options")
*/
private $optionQuestion;
public function __construct(){
$this->optionQuestion = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion){
$this->optionQuestion[] = $optionQuestion;
return $this;
}
public function removeOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion)
{
$this->optionQuestion->removeElement($optionQuestion);
}
public function getOptionQuestion(){
return $this->optionQuestion;
}
}
class Options2Questions
{
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Options", inversedBy="optionsQuestions")
* #ORM\JoinColumn(name="options_id", referencedColumnName="id", nullable=false)
*/
private $options;
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Question", inversedBy="optionsQuestions")
* #ORM\JoinColumn(name="question_id", referencedColumnName="id", nullable=false)
*/
private $question;
/**
* #var int
*
* #ORM\Column(name="rank", type="integer", nullable=true)
*/
private $rank;
/**
* Set rank
*
* #param integer $rank
*
* #return Options2Questions
*/
public function setRank($rank)
{
$this->rank = $rank;
return $this;
}
/**
* Get rank
*
* #return int
*/
public function getRank()
{
return $this->rank;
}
/**
* Set options
*
* #param \AppBundle\Entity\Options $options
*
* #return Options2Questions
*/
public function setOptions(\AppBundle\Entity\Options $options)
{
$this->options = $options;
return $this;
}
/**
* Get options
*
* #return \AppBundle\Entity\Options
*/
public function getOptions()
{
return $this->options;
}
/**
* Set question
*
* #param \AppBundle\Entity\Question $question
*
* #return Options2Questions
*/
public function setQuestion(\AppBundle\Entity\Question $question)
{
$this->question = $question;
return $this;
}
/**
* Get question
*
* #return \AppBundle\Entity\Question
*/
public function getQuestion()
{
return $this->question;
}
}
I could not find any documentation on this. Where should I look specifically?
First of all you have to create the admin class - service for the all of these three entities. Therefore you will have the next 3 classes:
a) QuestionAdmin
b) OptionAdmin
c) Options2QuestionsAdmin
If you want to remove from you admin services list the Options2QuestionsAdmin - you can add show_in_dashboard: false line to the services.yml:
app.admin.options2questions:
class: AppBundle\Options2questionsAdmin
arguments: [~, AppBundle\EntityOptions2questions, SonataAdminBundle:CRUD]
tags:
- { name: sonata.admin, manager_type: orm, group: 'your_group', label: 'your_label', show_in_dashboard: false }
In your QuestionAdmin class in formmapper method you can add this:
class QuestionAdmin extends AbstractAdmin
{
// ...
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('optionQuestion', 'sonata_type_collection', [
'required' => true,
'label' => 'option_question',
'by_reference' => false,
'btn_add' => 'add_button',
'type_options' => [
'delete' => true,
],
], [
'edit' => 'inline',
'inline' => 'standard',
'sortable' => 'id',
'allow_delete' => true,
])
;
}
I have this Entities:
<?php
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use ProductBundle\DBAL\Types\StatusType;
use ProductBundle\DBAL\Types\FieldType;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="product_detail")
*/
class ProductDetail {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="ProductDetail")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #ORM\Column(type="string", length=255)
*/
protected $label;
/**
* #var string $field_type
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\FieldType")
* #ORM\Column(name="field_type", type="FieldType", nullable=false)
*/
protected $field_type;
/**
* #ORM\Column(name="values_text", type="string")
*/
protected $values_text;
/**
* #ORM\Column(type="string", length=255)
*/
protected $measure_unit;
/**
* #var string $status
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\StatusType")
* #ORM\Column(name="status", type="StatusType", nullable=false)
*/
protected $status;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail")
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail")
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="group", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $detail_group;
public function __construct() {
$this->detail_group = new \Doctrine\Common\Collections\ArrayCollection();
$this->category = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(ProductDetail $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setLabel($label) {
$this->label = $label;
}
public function getLabel() {
return $this->label;
}
public function setFieldType($field_type) {
$this->field_type = $field_type;
}
public function getFieldType() {
return $this->field_type;
}
public function setValuesText($values_text) {
$this->values_text = $values_text;
}
public function getValuesText() {
return $this->values_text;
}
public function setMeasureUnit($measure_unit) {
$this->measure_unit = $measure_unit;
}
public function getMeasureUnit() {
return $this->measure_unit;
}
public function setStatus($status) {
$this->status = $status;
}
public function getStatus() {
return $this->status;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setCategory(Category $category) {
$this->category[] = $category;
}
public function getCategory() {
return $this->category;
}
public function setDetailGroup(DetailGroup $detailGroup) {
$this->detail_group[] = $detailGroup;
}
public function getDetailGroup() {
return $this->detail_group;
}
}
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="detail_group")
*/
class DetailGroup {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="DetailGroup")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\ProductDetail", mappedBy="detail_group", cascade={"all"})
*/
protected $productDetail;
public function __construct() {
$this->productDetail = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(DetailGroup $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setProductDetail(ProductDetail $productDetail) {
$this->productDetail[] = $productDetail;
}
public function getProductDetail() {
return $this->productDetail;
}
}
This is my ProductDetailType.php form:
<?php
namespace ProductBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProductDetailType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('category', 'entity', array('class' => 'CategoryBundle:Category', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('detail_group', 'entity', array('class' => 'ProductBundle:DetailGroup', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('parent', 'entity', array('class' => 'ProductBundle:ProductDetail', 'property' => 'label', 'required' => false))
->add('description', 'text', array('required' => false))
->add('label')
->add('field_type', 'choice', ['choices' => \ProductBundle\DBAL\Types\FieldType::getChoices()])
->add('values_text', 'collection', array('type' => 'text', 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false))
->add('measure_unit', 'text', array('required' => false))
->add('status', 'choice', ['choices' => \ProductBundle\DBAL\Types\StatusType::getChoices()]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ProductBundle\Entity\ProductDetail'
));
}
public function getName() {
return 'prod_detail_create';
}
}
And this is my createAction():
/**
* Handle product details creation
*
* #Route("/product_detail/create", name="product_detail_create")
* #Method("POST")
* #Template("ProductBundle:ProductDetail:new.html.twig")
*/
public function createAction(Request $request) {
$entity = new ProductDetail();
$form = $this->createForm(new ProductDetailType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity->setValuesText(serialize($form->get('values_text')->getData()));
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('product_detail_list'));
}
return $this->render('ProductBundle:ProductDetail:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
When I try to insert new records I get this errors:
An exception occurred while executing 'INSERT INTO
product_detail_has_category (category, detail) VALUES (?, ?)' with
params [7, 2]:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(product_detail_has_category, CONSTRAINT
fk_product_detail_has_category_product_detail1 FOREIGN KEY
(detail) REFERENCES product_detail (id) ON UPDATE CASCADE)
I check the logs and see this:
DEBUG - "START TRANSACTION"
DEBUG - INSERT INTO product_detail (description, label, field_type, values_text, measure_unit, status, created, modified, parent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
DEBUG - INSERT INTO product_detail_has_category (category, detail) VALUES (?, ?)
DEBUG - "ROLLBACK"
I don't know why it doesn't insert product_detail and the tries to create the relation, what is wrong?
You can either:
set on $category field set cascade={"PERSIST"} in #ManyToMany.
This requires no chnages to controller itself but it depends on whether you like defining cascades.
persist Category object manually before persisting ProductDetail.
$em = ...; // your entity manager
// first, manually persist any category that is found
foreach ( $productDetail->getCategory() as $c ){
$em->persist($c);
}
// then, persist top-level object
$em->persist($productDetail);
// finally, flush everything
$em->flush();
Hope this helps a bit...
I've found where the errors was, see the changes below in annotations:
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail", cascade={"persist"})
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail", cascade={"persist"})
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="kgroup", referencedColumnName="id")}
* )
*/
protected $detail_group;
I've inverted the relations in joinColumns and inversedJoinColumns and all works fine, also has to rename group to kgroup due to MySQL query errors!!