Why isn't my property mapped to the entity - symfony

i got a entity named Listing, here an overview of the table:
I tried to map the field "promoted" as the same as the field "certified", however this isn't working even if symfony tells me all is up-to-date with the db:
here's what i need to do with the "promoted" boolean field (same as "certified" field"), configure form fields:
$formMapper
->with('admin.listing.title')
->add(
'status',
ChoiceType::class,
array(
'choices' => array_flip(Listing::$statusValues),
'placeholder' => 'admin.listing.status.label',
'translation_domain' => 'cocorico_listing',
'label' => 'admin.listing.status.label',
)
)
->add(
'adminNotation',
ChoiceType::class,
array(
'choices' => array_combine(
range(0, 10, 0.5),
array_map(
function ($num) {
return number_format($num, 1);
},
range(0, 10, 0.5)
)
),
'placeholder' => 'admin.listing.admin_notation.label',
'label' => 'admin.listing.admin_notation.label',
'required' => false,
)
)
->add(
'certified',
null,
array(
'label' => 'admin.listing.certified.label',
)
)
->add(
'promoted',
null,
array(
'label' => 'admin.listing.promoted.label',
'required' => false,
)
)
for the type of the certified it is "null" and it works, it find the type as a boolean, but for the promoted it does not find it i got this error:
request.CRITICAL: Uncaught PHP Exception RuntimeException: "Please define a type for field promoted in Cocorico\ListingBundle\Admin\ListingAdmin" at /var/www/Symfony/vendor/sonata-project/doctrine-orm-admin-bundle/src/Builder/FormContractor.php line 69 {"exception":"[object] (RuntimeException(code: 0): Please define a type for field promoted in Cocorico\\ListingBundle\\Admin\\ListingAdmin at /var/www/Symfony/vendor/sonata-project/doctrine-orm-admin-bundle/src/Builder/FormContractor.php:69)"} []
here's my classe BaseListing.php:
<?php
/*
* This file is part of the Cocorico package.
*
* (c) Cocolabs SAS <contact#cocolabs.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cocorico\ListingBundle\Model;
use Cocorico\ListingBundle\Entity\Listing;
use Cocorico\ListingBundle\Validator\Constraints as CocoricoAssert;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Listing
*
* #CocoricoAssert\ListingConstraint()
*
* #ORM\MappedSuperclass
*/
abstract class BaseListing
{
/* Status */
const STATUS_NEW = 1;
const STATUS_PUBLISHED = 2;
const STATUS_INVALIDATED = 3;
const STATUS_SUSPENDED = 4;
const STATUS_DELETED = 5;
const STATUS_TO_VALIDATE = 6;
public static $statusValues = array(
self::STATUS_NEW => 'entity.listing.status.new',
self::STATUS_PUBLISHED => 'entity.listing.status.published',
self::STATUS_INVALIDATED => 'entity.listing.status.invalidated',
self::STATUS_SUSPENDED => 'entity.listing.status.suspended',
self::STATUS_DELETED => 'entity.listing.status.deleted',
self::STATUS_TO_VALIDATE => 'entity.listing.status.to_validate',
);
public static $visibleStatus = array(
self::STATUS_NEW,
self::STATUS_PUBLISHED,
self::STATUS_INVALIDATED,
self::STATUS_SUSPENDED,
self::STATUS_TO_VALIDATE,
);
/* Type */
const TYPE_ONE = 1;
const TYPE_TWO = 2;
const TYPE_THREE = 3;
public static $typeValues = array(
self::TYPE_ONE => 'entity.listing.type.one',
self::TYPE_TWO => 'entity.listing.type.two',
self::TYPE_THREE => 'entity.listing.type.three',
);
/* Cancellation policy */
const CANCELLATION_POLICY_FLEXIBLE = 1;
const CANCELLATION_POLICY_STRICT = 2;
public static $cancellationPolicyValues = array(
self::CANCELLATION_POLICY_FLEXIBLE => 'entity.listing.cancellation_policy.flexible',
self::CANCELLATION_POLICY_STRICT => 'entity.listing.cancellation_policy.strict',
);
public static $cancellationPolicyDescriptions = array(
self::CANCELLATION_POLICY_FLEXIBLE => 'entity.listing.cancellation_policy_desc.flexible',
self::CANCELLATION_POLICY_STRICT => 'entity.listing.cancellation_policy_desc.strict',
);
/**
* #ORM\Column(name="status", type="smallint", nullable=false)
*
* #var integer
*/
protected $status = self::STATUS_NEW;
/**
* #ORM\Column(name="type", type="smallint", nullable=true)
*
* #var integer
*/
protected $type;
/**
* #ORM\Column(name="price", type="decimal", precision=8, scale=0, nullable=false)
* #Assert\NotBlank(message="assert.not_blank")
*
* #var integer
*/
protected $price = 0;
/**
*
* #ORM\Column(name="certified", type="boolean", nullable=true)
*
* #var boolean
*/
protected $certified;
/**
* #var boolean $promoted
*
* #ORM\Column(name="promoted", type="boolean", nullable=true)
*
* #var boolean
*/
protected $promoted;
/**
*
* #ORM\Column(name="min_duration", type="smallint", nullable=true)
*
* #var integer
*/
protected $minDuration;
/**
*
* #ORM\Column(name="max_duration", type="smallint", nullable=true)
*
* #var integer
*/
protected $maxDuration;
/**
*
* #ORM\Column(name="cancellation_policy", type="smallint", nullable=false)
* #Assert\NotBlank(message="assert.not_blank")
*
* #var integer
*/
protected $cancellationPolicy = self::CANCELLATION_POLICY_FLEXIBLE;
/**
* #ORM\Column(name="average_rating", type="smallint", nullable=true)
*
* #var integer
*/
protected $averageRating;
/**
* #ORM\Column(name="comment_count", type="integer", nullable=true)
*
* #var integer
*/
protected $commentCount = 0;
/**
* Admin notation
*
* #ORM\Column(name="admin_notation", type="decimal", precision=3, scale=1, nullable=true)
*
* #var float
*/
protected $adminNotation;
/**
* #ORM\Column(name="availabilities_updated_at", type="datetime", nullable=true)
*
* #var \DateTime
*/
protected $availabilitiesUpdatedAt;
/**
* Translation proxy
*
* #param $method
* #param $arguments
* #return mixed
*/
public function __call($method, $arguments)
{
return $this->proxyCurrentLocaleTranslation($method, $arguments);
}
/**
* Set status
*
* #param integer $status
* #return $this
*/
public function setStatus($status)
{
if (!in_array($status, array_keys(self::$statusValues))) {
throw new \InvalidArgumentException(
sprintf('Invalid value for listing.status : %s.', $status)
);
}
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Get Status Text
*
* #return string
*/
public function getStatusText()
{
return self::$statusValues[$this->getStatus()];
}
/**
* Return available status for current status
*
* #param int $status
*
* #return array
*/
public static function getAvailableStatusValues($status)
{
$availableStatus = array(self::STATUS_DELETED);
if ($status == self::STATUS_NEW) {
$availableStatus[] = self::STATUS_PUBLISHED;
} elseif ($status == self::STATUS_PUBLISHED) {
$availableStatus[] = self::STATUS_SUSPENDED;
} elseif ($status == self::STATUS_INVALIDATED) {
$availableStatus[] = self::STATUS_TO_VALIDATE;
} elseif ($status == self::STATUS_SUSPENDED) {
$availableStatus[] = self::STATUS_PUBLISHED;
}
//Prepend current status to visible status
array_unshift($availableStatus, $status);
//Construct associative array with keys equals to status values and values to label of status
$status = array_intersect_key(
self::$statusValues,
array_flip($availableStatus)
);
return $status;
}
/**
* Set price
*
* #param integer $price
* #return $this
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* #return string
*/
public function getPrice()
{
return $this->price;
}
/**
* Get price
*
* #return float
*/
public function getPriceDecimal()
{
return $this->price / 100;
}
/**
* Get offerer amount fees
*
* #param array $feeAsOfferer
*
* #return float
*/
public function getAmountFeeAsOffererDecimal($feeAsOfferer)
{
return $this->getPriceDecimal() * $feeAsOfferer['percent'] + $feeAsOfferer['fixed'] / 100;
}
/**
* Get amount to pay to offerer
*
* #param array $feeAsOfferer
*
* #return float
*/
public function getAmountToPayToOffererDecimal($feeAsOfferer)
{
return $this->getPriceDecimal() - $this->getAmountFeeAsOffererDecimal($feeAsOfferer);
}
/**
* Get amount to pay to offerer minus VAT when listing price is VAT excluded.
*
* Return the same result than getAmountToPayToOffererDecimal used with listing price VAT is included:
* amountToPayVATIncluded = PriceVATIncluded - (PriceVATIncluded * feeAsOfferer)
* amountToPayVATExcluded = amountToPayVATIncluded / (1 + vatRate)
*
* So :
* amountToPayVATIncluded = ((price * (1 + vatRate)) - (price * (1 + vatRate) * feeAsOfferer))
* amountToPayVATExcluded = amountToPayVATIncluded / (1 + vatRate)
* amountToPayVATExcluded = price - price * feeAsOfferer
* amountToPayVATExcluded = getAmountToPayToOffererDecimal
*
*
* #param array $feeAsOfferer
*
* #return int
*/
public function amountToPayToOffererForPriceExcludingVATDecimal($feeAsOfferer)
{
return $this->getAmountToPayToOffererDecimal($feeAsOfferer);
}
/**
* Get offerer amount fees when listing price is VAT excluded.
* Fees are computed on listing price VAT included
*
* #param array $feeAsOfferer
* #param float $vatRate
*
* #return int
*/
public function getAmountFeeAsOffererForPriceExcludingVATDecimal($feeAsOfferer, $vatRate)
{
return $this->getAmountFeeAsOffererDecimal($feeAsOfferer) * (1 + $vatRate);
}
/**
* Return the min listing price according to offerer fees and default min price.
*
* #param array $defaultFeeAsOfferer
* #param int $defaultMinPrice
* #return float
*/
public function getMinPrice($defaultFeeAsOfferer, $defaultMinPrice)
{
$result = $defaultMinPrice;
/** #var $this Listing */
$user = $this->getUser();
if ($user) {
$feeAsOfferer = $user->getFeeAsOfferer($defaultFeeAsOfferer);
$result = max(round($feeAsOfferer['fixed'] / (1 - $feeAsOfferer['percent']), 2), $defaultMinPrice);
}
return $result;
}
/**
* #param array $defaultFeeAsOfferer
* #param int $defaultMinPrice
* #return float
*/
public function getMinPriceDecimal($defaultFeeAsOfferer, $defaultMinPrice)
{
return $this->getMinPrice($defaultFeeAsOfferer, $defaultMinPrice) / 100;
}
/**
* Check if listing has correct min price according to offerer fees and default min price
*
* #param array $defaultFeeAsOfferer
* #param int $defaultMinPrice
* #return bool
*/
public function hasMinPrice($defaultFeeAsOfferer, $defaultMinPrice)
{
if ($defaultMinPrice > 0 || $this->getPrice() > 0) {//if listing is not free or min price > 0
$minPrice = $this->getMinPrice($defaultFeeAsOfferer, $defaultMinPrice);
if ($this->getPrice() < $minPrice) {
return false;
}
}
//else listing is free
return true;
}
/**
* #return boolean
*/
public function isCertified()
{
return $this->certified;
}
/**
* #param boolean $certified
*/
public function setCertified($certified)
{
$this->certified = $certified;
}
/**
* #return boolean
*/
public function isPromoted()
{
return $this->promoted;
}
/**
* #param boolean $promoted
*/
public function setPromoted($promoted)
{
$this->promoted = $promoted;
}
/**
* #return int
*/
public function getType()
{
return $this->type;
}
/**
* Set type
*
* #param integer $type
* #return $this
*/
public function setType($type)
{
if (!in_array($type, array_keys(self::$typeValues))) {
throw new \InvalidArgumentException(
sprintf('Invalid value for listing.type : %s.', $type)
);
}
$this->type = $type;
return $this;
}
/**
* Get Type Text
*
* #return string
*/
public function getTypeText()
{
return self::$typeValues[$this->getType()];
}
/**
* Get certified
*
* #return boolean
*/
public function getCertified()
{
return $this->certified;
}
/**
* Get promoted
*
* #return boolean
*/
public function getPromoted()
{
return $this->promoted;
}
/**
* #return int
*/
public function getMinDuration()
{
return $this->minDuration;
}
/**
* #param int $minDuration
*/
public function setMinDuration($minDuration)
{
$this->minDuration = $minDuration;
}
/**
* #return int
*/
public function getMaxDuration()
{
return $this->maxDuration;
}
/**
* #param int $maxDuration
*/
public function setMaxDuration($maxDuration)
{
$this->maxDuration = $maxDuration;
}
/**
* #return int
*/
public function getCancellationPolicy()
{
return $this->cancellationPolicy;
}
/**
* #param int $cancellationPolicy
*
* #return BaseListing
*/
public function setCancellationPolicy($cancellationPolicy)
{
if (!in_array($cancellationPolicy, array_keys(self::$cancellationPolicyValues))) {
throw new \InvalidArgumentException(
sprintf('Invalid value for listing.status : %s.', $cancellationPolicy)
);
//$cancellationPolicy = self::CANCELLATION_POLICY_FLEXIBLE;
}
$this->cancellationPolicy = $cancellationPolicy;
return $this;
}
/**
* Get Cancellation Policy Text
*
* #return string
*/
public function getCancellationPolicyText()
{
return self::$cancellationPolicyValues[$this->getCancellationPolicy()];
}
/**
* Get Cancellation Policy Description
*
* #return string
*/
public function getCancellationPolicyDescription()
{
return self::$cancellationPolicyDescriptions[$this->getCancellationPolicy()];
}
/**
* Set averageRating
*
* #param integer $averageRating
* #return $this
*/
public function setAverageRating($averageRating)
{
$this->averageRating = $averageRating;
return $this;
}
/**
* Get averageRating
*1
*
* #return integer
*/
public function getAverageRating()
{
return $this->averageRating;
}
/**
* Set commentCount
*
* #param integer $commentCount
* #return $this
*/
public function setCommentCount($commentCount)
{
$this->commentCount = $commentCount;
return $this;
}
/**
* Get commentCount
*1
*
* #return integer
*/
public function getCommentCount()
{
return $this->commentCount;
}
/**
* #return float
*/
public function getAdminNotation()
{
return $this->adminNotation;
}
/**
* #param float $adminNotation
*/
public function setAdminNotation($adminNotation)
{
$this->adminNotation = $adminNotation;
}
/**
* #return \DateTime
*/
public function getAvailabilitiesUpdatedAt()
{
return $this->availabilitiesUpdatedAt;
}
/**
* #param \DateTime $availabilitiesUpdatedAt
*/
public function setAvailabilitiesUpdatedAt($availabilitiesUpdatedAt)
{
$this->availabilitiesUpdatedAt = $availabilitiesUpdatedAt;
}
/**
* #return bool
*/
public function hasPrice()
{
return $this->getPrice() > 0;
}
}
Here's Entity\Listing.php:
<?php
namespace Cocorico\ListingBundle\Entity;
use Cocorico\BookingBundle\Entity\Booking;
use Cocorico\ListingBundle\Model\BaseListing;
use Cocorico\ListingBundle\Model\ListingOptionInterface;
use Cocorico\ListingCategoryBundle\Entity\ListingListingCategory;
use Cocorico\ListingCharacteristicBundle\Entity\ListingListingCharacteristic;
use Cocorico\ListingDiscountBundle\Entity\ListingDiscount;
use Cocorico\ListingImageBundle\Entity\ListingImage;
use Cocorico\ListingLocationBundle\Entity\ListingLocation;
use Cocorico\MessageBundle\Entity\Thread;
use Cocorico\UserBundle\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Listing
*
* #ORM\Entity(repositoryClass="Cocorico\ListingBundle\Repository\ListingRepository")
*
* #ORM\Table(name="listing",indexes={
* #ORM\Index(name="created_at_l_idx", columns={"created_at"}),
* #ORM\Index(name="status_l_idx", columns={"status"}),
* #ORM\Index(name="price_idx", columns={"price"}),
* #ORM\Index(name="type_idx", columns={"type"}),
* #ORM\Index(name="min_duration_idx", columns={"min_duration"}),
* #ORM\Index(name="max_duration_idx", columns={"max_duration"}),
* #ORM\Index(name="average_rating_idx", columns={"average_rating"}),
* #ORM\Index(name="admin_notation_idx", columns={"admin_notation"}),
* #ORM\Index(name="platform_notation_idx", columns={"platform_notation"}),
* })
*/
class Listing extends BaseListing
{
use ORMBehaviors\Timestampable\Timestampable;
use ORMBehaviors\Translatable\Translatable;
use \Cocorico\ListingSearchAdvancedBundle\Model\ListingSearchableTrait;
// use \Cocorico\ListingCategoryFieldBundle\Model\ListingCategoryFieldableTrait;
// use \Cocorico\DeliveryBundle\Model\ListingDeliverableTrait;
// use \Cocorico\ListingDepositBundle\Model\ListingDepositableTrait;
// use \Cocorico\ListingSessionBundle\Model\ListingSessionableTrait;
// use \Cocorico\ServiceBundle\Model\ListingTrait;
// use \Cocorico\ListingVideoBundle\Model\ListingVideoTrait;
// use \Cocorico\CarrierBundle\Model\ListingCarrierableTrait;
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Cocorico\CoreBundle\Model\CustomIdGenerator")
*
* #var integer
*/
protected $id;
/**
* #Assert\NotBlank(message="assert.not_blank")
*
* #ORM\ManyToOne(targetEntity="Cocorico\UserBundle\Entity\User", inversedBy="listings", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*
* #var User
*/
protected $user;
/**
* #ORM\OneToOne(targetEntity="Cocorico\ListingLocationBundle\Entity\ListingLocation", inversedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\JoinColumn(name="location_id", referencedColumnName="id", onDelete="CASCADE")
*
* #var ListingLocation
**/
protected $location;
/**
* #ORM\OneToMany(targetEntity="Cocorico\ListingCategoryBundle\Entity\ListingListingCategory", mappedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true)//, fetch="EAGER"
*
*/
protected $listingListingCategories;
/**
* For Asserts #see \Cocorico\ListingBundle\Validator\Constraints\ListingValidator
*
* #ORM\OneToMany(targetEntity="Cocorico\ListingImageBundle\Entity\ListingImage", mappedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\OrderBy({"position" = "asc"})
*/
protected $images;
/**
* #ORM\OneToMany(targetEntity="Cocorico\ListingCharacteristicBundle\Entity\ListingListingCharacteristic", mappedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true) //, fetch="EAGER"
*
*/
protected $listingListingCharacteristics;
/**
*
* #ORM\OneToMany(targetEntity="Cocorico\ListingDiscountBundle\Entity\ListingDiscount", mappedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\OrderBy({"fromQuantity" = "asc"})
*/
protected $discounts;
/**
* #ORM\OneToMany(targetEntity="Cocorico\BookingBundle\Entity\Booking", mappedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\OrderBy({"createdAt" = "desc"})
*/
protected $bookings;
/**
* #ORM\OneToMany(targetEntity="Cocorico\MessageBundle\Entity\Thread", mappedBy="listing", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"createdAt" = "desc"})
*/
protected $threads;
/**
*
* #ORM\OneToMany(targetEntity="Cocorico\ListingBundle\Model\ListingOptionInterface", mappedBy="listing", cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $options;
public function __construct()
{
$this->images = new ArrayCollection();
$this->listingListingCharacteristics = new ArrayCollection();
$this->listingListingCategories = new ArrayCollection();
$this->discounts = new ArrayCollection();
$this->bookings = new ArrayCollection();
$this->threads = new ArrayCollection();
$this->options = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add characteristics
*
* #param ListingListingCharacteristic $listingListingCharacteristic
* #return Listing
*/
public function addListingListingCharacteristic(ListingListingCharacteristic $listingListingCharacteristic)
{
$this->listingListingCharacteristics[] = $listingListingCharacteristic;
return $this;
}
/**
* Remove characteristics
*
* #param ListingListingCharacteristic $listingListingCharacteristic
*/
public function removeListingListingCharacteristic(ListingListingCharacteristic $listingListingCharacteristic)
{
$this->listingListingCharacteristics->removeElement($listingListingCharacteristic);
$listingListingCharacteristic->setListing(null);
}
/**
* Get characteristics
*
* #return \Doctrine\Common\Collections\Collection|ListingListingCharacteristic[]
*/
public function getListingListingCharacteristics()
{
return $this->listingListingCharacteristics;
}
/**
* Get characteristics ordered by Group and Characteristic
*
* #return ArrayCollection
*/
public function getListingListingCharacteristicsOrderedByGroup()
{
$iterator = $this->listingListingCharacteristics->getIterator();
$iterator->uasort(
function ($a, $b) {
/**
* #var ListingListingCharacteristic $a
* #var ListingListingCharacteristic $b
*/
$groupPosA = $a->getListingCharacteristic()->getListingCharacteristicGroup()->getPosition();
$groupPosB = $b->getListingCharacteristic()->getListingCharacteristicGroup()->getPosition();
$characteristicPosA = $a->getListingCharacteristic()->getPosition();
$characteristicPosB = $b->getListingCharacteristic()->getPosition();
if ($groupPosA == $groupPosB) {
if ($characteristicPosA == $characteristicPosB) {
return 0;
}
return ($characteristicPosA < $characteristicPosB) ? -1 : 1;
}
return ($groupPosA < $groupPosB) ? -1 : 1;
}
);
return new ArrayCollection(iterator_to_array($iterator));
}
/**
* Add characteristics
*
* #param ListingListingCharacteristic $listingListingCharacteristic
* #return Listing
*/
public function addListingListingCharacteristicsOrderedByGroup(
ListingListingCharacteristic $listingListingCharacteristic
) {
return $this->addListingListingCharacteristic($listingListingCharacteristic);
}
/**
* Remove characteristics
*
* #param ListingListingCharacteristic $listingListingCharacteristic
*/
public function removeListingListingCharacteristicsOrderedByGroup(
ListingListingCharacteristic $listingListingCharacteristic
) {
$this->removeListingListingCharacteristic($listingListingCharacteristic);
}
/**
* Add category
*
* #param ListingListingCategory $listingListingCategory
* #return Listing
*/
public function addListingListingCategory(ListingListingCategory $listingListingCategory)
{
$listingListingCategory->setListing($this);
$this->listingListingCategories[] = $listingListingCategory;
return $this;
}
/**
* Remove category
*
* #param ListingListingCategory $listingListingCategory
*/
public function removeListingListingCategory(ListingListingCategory $listingListingCategory)
{
// foreach ($listingListingCategory->getValues() as $value) {
// $listingListingCategory->removeValue($value);
// }
$this->listingListingCategories->removeElement($listingListingCategory);
}
/**
* Get categories
*
* #return \Doctrine\Common\Collections\Collection|ListingListingCategory[]
*/
public function getListingListingCategories()
{
return $this->listingListingCategories;
}
/**
* Set user
*
* #param \Cocorico\UserBundle\Entity\User $user
* #return Listing
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \Cocorico\UserBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
}
i have done the migration and validate schem, doctrine says it's all good:
However i can replace the 'null' for my field "promoted" by CheckBoxType::class, then i got no error and it looks like the certified:
But this is not working with the db, not persisiting the boolean in the db like Certified.. if someone know something that can help i'll be very happy, been stuck on this for a while..
EDIT: i found that BaseListing isn't included in the mapping such as BaseUser or BaseGroup:
This is maybe the cause, how could i include this please ?

Related

Multiple-File upload - symfony3

Symfony3 with PhpStorm.2016.3.2
I succeeded in making a file uploader for one picture only. But now I need to make it "multiple"
I will show you the code and the error that comes up with it as I canno't upload multiple files.
here is the controller
public function newAction(Request $request)
{
$restaurant = new Restaurant();
$form = $this->createForm(RestaurantType::class, $restaurant);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
// file upload
if ($request->files->get('restaurant')['picture'] != null) {
$file = $request->files->get('restaurant')['picture'];
$targetDir = $this->getParameter('uploaded_restaurants');
$filename = $this->get('app.uploader')->upload($file, $targetDir);
$mediaRestaurant = null;
if ($restaurant->getId() != null) {
$mediaRestaurant = $this->getDoctrine()->getRepository('AppBundle:Media')->findPictureByRestaurant($restaurant);
}
if ($mediaRestaurant) {
$media = $mediaRestaurant;
$fs = new Filesystem();
try {
$fs->remove($this->get('kernel')->getRootDir().'/../web/uploads/restaurants/'.$media->getName());
} catch (IOException $e) {
echo "error";
}
} else {
$media = new Media();
$media->setCreatedAt(new \DateTime());
}
$originalName = $file->getClientOriginalName();
$media->setName($filename)
->setOriginalName($originalName)
->setType('img')
->setContext('restaurant_picture')
->setUpdatedAt(new \DateTime())
;
$media->setRestaurant($restaurant);
$em->persist($media);
}
$restaurant->setIsActivated(false);
$em->persist($restaurant);
$em->flush();
}
return $this->render('admin/restaurant/new.html.twig', array(
'restaurant' => $restaurant,
'form' => $form->createView(),
));
}
my FormType(called RestaurantType) where I added a field for file upload (when I set up multiple to false it actually works for only one picture)
$builder
->add('picture', FileType::class, array(
'label' => 'Photos du restaurant',
'multiple' => true,
'required' => false,
'mapped' => false,
'attr' => array(
'accept' => '.jpg,.jpeg,.png'),
))
The service to uploads file
class FileUploader
{
/**
* #param UploadedFile $file
* #param $targetDir
* #return string
*/
public function upload(UploadedFile $file, $targetDir)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($targetDir, $fileName);
return $fileName;
}
}
the queryBuilder in the MediaRepository
public function findPictureByRestaurant(Restaurant $restaurant)
{
return $this->createQueryBuilder('m')
->select('m')
->where('m.restaurant = :restaurant')
->setParameter('restaurant', $restaurant)
->andWhere('m.context = :restaurant_picture')
->setParameter('restaurant_picture', 'restaurant_picture')
->getQuery()
->getOneOrNullResult();
}
in my show.html.twig where you can see the picture
{% if restaurant.medias != null and restaurant.medias.count > 0 and restaurant.medias[0].name != null %}
<img src="/uploads/restaurants/{{ restaurant.medias[0].name }}">
{% endif %}
my config.yml where the files are uploaded
parameters:
locale: fr
uploaded_restaurants: "%kernel.root_dir%/../web/uploads/restaurants"
And the entity Restaurant which is pretty long (sorry)
/**
* Restaurant
*
* #ORM\Table(name="restaurant")
* #ORM\Entity(repositoryClass="AppBundle\Repository\RestaurantRepository")
*/
class Restaurant
{
/**
* #var FoodType
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\FoodType", inversedBy="restaurants")
* #ORM\JoinColumn(name="food_type_id", referencedColumnName="id")
*/
private $foodType;
/**
* #var City
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\City", inversedBy="restaurants")
* #ORM\JoinColumn(name="city_id", referencedColumnName="id")
*/
private $city;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Media", mappedBy="restaurant")
*/
private $medias;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Privatisation", mappedBy="restaurant")
*/
private $privatisations;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Retrocession", mappedBy="restaurant")
*/
private $retrocessions;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\OpenedSlot", mappedBy="restaurant")
*/
private $openedSlots;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\ExceptionSlot", mappedBy="restaurant")
*/
private $exceptionSlots;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Slot", mappedBy="restaurant")
*/
private $slots;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Chef", mappedBy="restaurant")
*/
private $chefs;
/**
* Constructor
*/
public function __construct()
{
$this->medias = new ArrayCollection();
$this->privatisations = new ArrayCollection();
$this->retrocessions = new ArrayCollection();
$this->openedSlots = new ArrayCollection();
$this->exceptionSlots = new ArrayCollection();
$this->slots = new ArrayCollection();
$this->chefs = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=120, unique=true)
* #Assert\Length(
* max = 120,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="slug", type="string", length=255, unique=true)
* #Gedmo\Slug(fields={"name"})
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $slug;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="webUrl", type="string", length=255, nullable=true)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $webUrl;
/**
* #var string
*
* #ORM\Column(name="tripAdvisorUrl", type="string", length=255, nullable=true)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $tripAdvisorUrl;
/**
* #var string
*
* #ORM\Column(name="facebookUrl", type="string", length=255, nullable=true)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $facebookUrl;
/**
* #var string
*
* #ORM\Column(name="twitterUrl", type="string", length=255, nullable=true)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $twitterUrl;
/**
* #var string
*
* #ORM\Column(name="instagramUrl", type="string", length=255, nullable=true)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $instagramUrl;
/**
* #var string
*
* #ORM\Column(name="phone", type="string", length=20)
* #Assert\Regex(
* pattern="#^0[1-9]([-. ]?[0-9]{2}){4}$#",
* match=true,
* message="Numéro de téléphone invalide."
* )
*/
private $phone;
/**
* #var string
*
* #ORM\Column(name="phone2", type="string", length=20, nullable=true)
* #Assert\Regex(
* pattern="#^0[1-9]([-. ]?[0-9]{2}){4}$#",
* match=true,
* message="Numéro de téléphone invalide."
* )
*/
private $phone2;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $email;
/**
* #var float
*
* #ORM\Column(name="latitude", type="float")
* #Assert\Regex(
* pattern="/^-?(?:\d+|\d*\.\d+)$/",
* match=true,
* )
*/
private $latitude;
/**
* #var float
*
* #ORM\Column(name="longitude", type="float")
* #Assert\Regex(
* pattern="/^-?(?:\d+|\d*\.\d+)$/",
* match=true,
* )
*/
private $longitude;
/**
* #var int
*
* #ORM\Column(name="stars", type="integer", nullable=true)
* #Assert\Regex(
* pattern="/^[0-9]+$/",
* match=true,
* message="Ceci n'est pas un chiffre."
* )
*/
private $stars;
/**
* #var int
*
* #ORM\Column(name="seatNumber", type="integer", nullable=true)
* #Assert\Regex(
* pattern="/^[0-9]+$/",
* match=true,
* )
*/
private $seatNumber;
/**
* #var float
*
* #ORM\Column(name="minPrice", type="float", nullable=true)
* #Assert\Regex(
* pattern="/^-?(?:\d+|\d*\.\d+)$/",
* match=true,
* )
*/
private $minPrice;
/**
* #var float
*
* #ORM\Column(name="maxPrice", type="float", nullable=true)
* #Assert\Regex(
* pattern="/^-?(?:\d+|\d*\.\d+)$/",
* match=true,
* )
*/
private $maxPrice;
/**
* #var string
*
* #ORM\Column(name="address", type="string", length=255)
* #Assert\Length(
* max = 255,
* maxMessage = "Ce champ ne peut pas dépasser {{ limit }} caractères."
* )
*/
private $address;
/**
* #var bool
*
* #ORM\Column(name="is_activated", type="boolean")
*/
private $isActivated = true;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Restaurant
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set slug
*
* #param string $slug
*
* #return Restaurant
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set description
*
* #param string $description
*
* #return Restaurant
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set webUrl
*
* #param string $webUrl
*
* #return Restaurant
*/
public function setWebUrl($webUrl)
{
$this->webUrl = $webUrl;
return $this;
}
/**
* Get webUrl
*
* #return string
*/
public function getWebUrl()
{
return $this->webUrl;
}
/**
* Set tripAdvisorUrl
*
* #param string $tripAdvisorUrl
*
* #return Restaurant
*/
public function setTripAdvisorUrl($tripAdvisorUrl)
{
$this->tripAdvisorUrl = $tripAdvisorUrl;
return $this;
}
/**
* Get tripAdvisorUrl
*
* #return string
*/
public function getTripAdvisorUrl()
{
return $this->tripAdvisorUrl;
}
/**
* Set facebookUrl
*
* #param string $facebookUrl
*
* #return Restaurant
*/
public function setFacebookUrl($facebookUrl)
{
$this->facebookUrl = $facebookUrl;
return $this;
}
/**
* Get facebookUrl
*
* #return string
*/
public function getFacebookUrl()
{
return $this->facebookUrl;
}
/**
* Set twitterUrl
*
* #param string $twitterUrl
*
* #return Restaurant
*/
public function setTwitterUrl($twitterUrl)
{
$this->twitterUrl = $twitterUrl;
return $this;
}
/**
* Get twitterUrl
*
* #return string
*/
public function getTwitterUrl()
{
return $this->twitterUrl;
}
/**
* Set instagramUrl
*
* #param string $instagramUrl
*
* #return Restaurant
*/
public function setInstagramUrl($instagramUrl)
{
$this->instagramUrl = $instagramUrl;
return $this;
}
/**
* Get instagramUrl
*
* #return string
*/
public function getInstagramUrl()
{
return $this->instagramUrl;
}
/**
* Set phone
*
* #param string $phone
*
* #return Restaurant
*/
public function setPhone($phone)
{
$this->phone = $phone;
return $this;
}
/**
* Get phone
*
* #return string
*/
public function getPhone()
{
return $this->phone;
}
/**
* Set phone2
*
* #param string $phone2
*
* #return Restaurant
*/
public function setPhone2($phone2)
{
$this->phone2 = $phone2;
return $this;
}
/**
* Get phone2
*
* #return string
*/
public function getPhone2()
{
return $this->phone2;
}
/**
* Set email
*
* #param string $email
*
* #return Restaurant
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set latitude
*
* #param float $latitude
*
* #return Restaurant
*/
public function setLatitude($latitude)
{
$this->latitude = $latitude;
return $this;
}
/**
* Get latitude
*
* #return float
*/
public function getLatitude()
{
return $this->latitude;
}
/**
* Set longitude
*
* #param float $longitude
*
* #return Restaurant
*/
public function setLongitude($longitude)
{
$this->longitude = $longitude;
return $this;
}
/**
* Get longitude
*
* #return float
*/
public function getLongitude()
{
return $this->longitude;
}
/**
* Set stars
*
* #param integer $stars
*
* #return Restaurant
*/
public function setStars($stars)
{
$this->stars = $stars;
return $this;
}
/**
* Get stars
*
* #return int
*/
public function getStars()
{
return $this->stars;
}
/**
* Set seatNumber
*
* #param integer $seatNumber
*
* #return Restaurant
*/
public function setSeatNumber($seatNumber)
{
$this->seatNumber = $seatNumber;
return $this;
}
/**
* Get seatNumber
*
* #return int
*/
public function getSeatNumber()
{
return $this->seatNumber;
}
/**
* Set minPrice
*
* #param float $minPrice
*
* #return Restaurant
*/
public function setMinPrice($minPrice)
{
$this->minPrice = $minPrice;
return $this;
}
/**
* Get minPrice
*
* #return float
*/
public function getMinPrice()
{
return $this->minPrice;
}
/**
* Set maxPrice
*
* #param float $maxPrice
*
* #return Restaurant
*/
public function setMaxPrice($maxPrice)
{
$this->maxPrice = $maxPrice;
return $this;
}
/**
* Get maxPrice
*
* #return float
*/
public function getMaxPrice()
{
return $this->maxPrice;
}
/**
* Set address
*
* #param string $address
*
* #return Restaurant
*/
public function setAddress($address)
{
$this->address = $address;
return $this;
}
/**
* Get address
*
* #return string
*/
public function getAddress()
{
return $this->address;
}
/**
* Add media
*
* #param Media $media
*
* #return Restaurant
*/
public function addMedia(Media $media)
{
$this->medias[] = $media;
return $this;
}
/**
* Remove media
*
* #param Media $media
*/
public function removeMedia(Media $media)
{
$this->medias->removeElement($media);
}
/**
* Get medias
*
* #return ArrayCollection
*/
public function getMedias()
{
return $this->medias;
}
/**
* Set foodType
*
* #param FoodType $foodType
*
* #return Restaurant
*/
public function setFoodType(FoodType $foodType = null)
{
$this->foodType = $foodType;
return $this;
}
/**
* Get foodType
*
* #return FoodType
*/
public function getFoodType()
{
return $this->foodType;
}
/**
* Add privatisation
*
* #param Privatisation $privatisation
*
* #return Restaurant
*/
public function addPrivatisation(Privatisation $privatisation)
{
$this->privatisations[] = $privatisation;
return $this;
}
/**
* Remove privatisation
*
* #param Privatisation $privatisation
*/
public function removePrivatisation(Privatisation $privatisation)
{
$this->privatisations->removeElement($privatisation);
}
/**
* Get privatisations
*
* #return ArrayCollection
*/
public function getPrivatisations()
{
return $this->privatisations;
}
/**
* Add retrocession
*
* #param Retrocession $retrocession
*
* #return Restaurant
*/
public function addRetrocession(Retrocession $retrocession)
{
$this->retrocessions[] = $retrocession;
return $this;
}
/**
* Remove retrocession
*
* #param Retrocession $retrocession
*/
public function removeRetrocession(Retrocession $retrocession)
{
$this->retrocessions->removeElement($retrocession);
}
/**
* Get retrocessions
*
* #return ArrayCollection
*/
public function getRetrocessions()
{
return $this->retrocessions;
}
/**
* Add openedSlot
*
* #param OpenedSlot $openedSlot
*
* #return Restaurant
*/
public function addOpenedSlot(OpenedSlot $openedSlot)
{
$this->openedSlots[] = $openedSlot;
return $this;
}
/**
* Remove openedSlot
*
* #param OpenedSlot $openedSlot
*/
public function removeOpenedSlot(OpenedSlot $openedSlot)
{
$this->openedSlots->removeElement($openedSlot);
}
/**
* Get openedSlots
*
* #return ArrayCollection
*/
public function getOpenedSlots()
{
return $this->openedSlots;
}
/**
* Add exceptionSlot
*
* #param ExceptionSlot $exceptionSlot
*
* #return Restaurant
*/
public function addExceptionSlot(ExceptionSlot $exceptionSlot)
{
$this->exceptionSlots[] = $exceptionSlot;
return $this;
}
/**
* Remove exceptionSlot
*
* #param ExceptionSlot $exceptionSlot
*/
public function removeExceptionSlot(ExceptionSlot $exceptionSlot)
{
$this->exceptionSlots->removeElement($exceptionSlot);
}
/**
* Get exceptionSlots
*
* #return ArrayCollection
*/
public function getExceptionSlots()
{
return $this->exceptionSlots;
}
/**
* Add slot
*
* #param Slot $slot
*
* #return Restaurant
*/
public function addSlot(Slot $slot)
{
$this->slots[] = $slot;
return $this;
}
/**
* Remove slot
*
* #param Slot $slot
*/
public function removeSlot(Slot $slot)
{
$this->slots->removeElement($slot);
}
/**
* Get slots
*
* #return ArrayCollection
*/
public function getSlots()
{
return $this->slots;
}
/**
* Add chef
*
* #param Chef $chef
*
* #return Restaurant
*/
public function addChef(Chef $chef)
{
$this->chefs[] = $chef;
return $this;
}
/**
* Remove chef
*
* #param Chef $chef
*/
public function removeChef(Chef $chef)
{
$this->chefs->removeElement($chef);
}
/**
* Get chefs
*
* #return ArrayCollection
*/
public function getChefs()
{
return $this->chefs;
}
/**
* Set city
*
* #param City $city
*
* #return Restaurant
*/
public function setCity(City $city = null)
{
$this->city = $city;
return $this;
}
/**
* Get city
*
* #return City
*/
public function getCity()
{
return $this->city;
}
/**
* #return bool
*/
public function getIsActivated()
{
return $this->isActivated;
}
/**
* #param bool $isActivated
*/
public function setIsActivated($isActivated)
{
$this->isActivated = $isActivated;
}
}
and the error that comes up when I upload a picture and click on submit button
To sum up it up all, this code works for only one picture when I set up multiple to false in my FormType. But then I am stuck for the multiple file upload and I can't get to find a way around that. Does someone know how to handle it with the code I given you?
Thank you
Because your file definition accept multiple uploads, you need to modify upload part of your controller:
// file upload
if ($request->files->get('restaurant')['picture'] != null) {
$files = $request->files->get('restaurant')['picture'];
foreach ($files as $file) {
$targetDir = $this->getParameter('uploaded_restaurants');
$filename = $this->get('app.uploader')->upload($file, $targetDir);
$mediaRestaurant = null;
if ($restaurant->getId() != null) {
$mediaRestaurant = $this->getDoctrine()->getRepository('AppBundle:Media')->findPictureByRestaurant($restaurant);
}
if ($mediaRestaurant) {
$media = $mediaRestaurant;
$fs = new Filesystem();
try {
$fs->remove($this->get('kernel')->getRootDir().'/../web/uploads/restaurants/'.$media->getName());
} catch (IOException $e) {
echo "error";
}
} else {
$media = new Media();
$media->setCreatedAt(new \DateTime());
}
$originalName = $file->getClientOriginalName();
$media->setName($filename)
->setOriginalName($originalName)
->setType('img')
->setContext('restaurant_picture')
->setUpdatedAt(new \DateTime())
;
$media->setRestaurant($restaurant);
$em->persist($media);
}
}
Notify that I've taken all files from form uploaded data first, and run your code in a foreach loop.
You need to handle an array in your upload method, as itll get that from the form, rather than an individual UploadedFile object.
/**
* #param UploadedFile $file
* #param $targetDir
* #return array
*/
public function upload($files, $targetDir)
{
if(!is_array($files)) {
$files = (array) $files; // cast to array in case of a form that isn't multiple.
}
$filenames = [];
foreach($files as $file) {
$filenames[] = md5(uniqid()).'.'.$file->guessExtension();
$file->move($targetDir, $fileName);
}
return $filenames;
}
youll need to twiddle whatever is getting the return filename variable, as it'll now be an array.
The other way, which in my opinion is a better UX, is to use a CollectionType and render a file input box per file. Heres a quick example.
$builder->add('file_uploads', CollectionType::class, [
'entry_type' => FileType::class,
'entry_options' => [
'label' => 'Photos du restaurant',
'multiple' => true,
'required' => false,
'mapped' => false,
'attr' => [
'accept' => '.jpg,.jpeg,.png',
],
'allow_add' => true,
'allow_delete' => true,
],
'prototype' => true,
]);
Youll then need to handle the resulting ArrayCollection in your upload handler. But this gives a much nicer interface for the user.
Ive not tested this snippet, so you might have to debug or adapt it to fit with what you're doing.

error in symfony for manyToOne and form imbricated

I encountered a problem under symfony3 which has blocked me for a while ... I do not understand this error:
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Project#$participants", got "string" instead.
Here is my entity Project:
class Project
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Participant", mappedBy="project", cascade={"persist", "remove"})
*/
private $participants;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text", nullable=true)
*/
private $content;
/**
* #var string
*
* #ORM\Column(name="place", type="string", length=255, nullable=true)
*/
private $place;
/**
* #var \DateTime
*
* #ORM\Column(name="dateEvent", type="datetime", nullable=true)
*/
private $dateEvent;
/**
* #var boolean
*
* #ORM\Column(name="status", type="boolean")
*/
private $status;
/**
* #var boolean
*
* #ORM\Column(name="in_progress", type="boolean")
*/
private $inProgress;
/**
* #var boolean
*
* #ORM\Column(name="accept_list", type="boolean")
*/
private $acceptList;
/**
* #var boolean
*
* #ORM\Column(name="visible_list", type="boolean")
*/
private $visibleList;
/**
* #var boolean
*
* #ORM\Column(name="many_loop", type="boolean")
*/
private $manyLoop;
/**
* #var string
*
* #ORM\Column(name="text_email", type="text", nullable=true)
*/
private $textEmail;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
public function __construct()
{
$this->run = false;
$this->status = false;
$this->acceptList = true;
$this->visibleList = true;
$this->inProgress = false;
$this->manyLoop = true;
$this->created = new \Datetime('now');
$this->participants = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Project
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Project
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set place
*
* #param string $place
*
* #return Project
*/
public function setPlace($place)
{
$this->place = $place;
return $this;
}
/**
* Get place
*
* #return string
*/
public function getPlace()
{
return $this->place;
}
/**
* Set dateEvent
*
* #param \DateTime $dateEvent
*
* #return Project
*/
public function setDateEvent($dateEvent)
{
$this->dateEvent = $dateEvent;
return $this;
}
/**
* Get dateEvent
*
* #return \DateTime
*/
public function getDateEvent()
{
return $this->dateEvent;
}
/**
* Set status
*
* #param boolean $status
*
* #return Project
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return boolean
*/
public function getStatus()
{
return $this->status;
}
/**
* Set inProgress
*
* #param boolean $inProgress
*
* #return Project
*/
public function setInProgress($inProgress)
{
$this->inProgress = $inProgress;
return $this;
}
/**
* Get inProgress
*
* #return boolean
*/
public function getInProgress()
{
return $this->inProgress;
}
/**
* Set acceptList
*
* #param boolean $acceptList
*
* #return Project
*/
public function setAcceptList($acceptList)
{
$this->acceptList = $acceptList;
return $this;
}
/**
* Get acceptList
*
* #return boolean
*/
public function getAcceptList()
{
return $this->acceptList;
}
/**
* Set visibleList
*
* #param boolean $visibleList
*
* #return Project
*/
public function setVisibleList($visibleList)
{
$this->visibleList = $visibleList;
return $this;
}
/**
* Get visibleList
*
* #return boolean
*/
public function getVisibleList()
{
return $this->visibleList;
}
/**
* Set manyLoop
*
* #param boolean $manyLoop
*
* #return Project
*/
public function setManyLoop($manyLoop)
{
$this->manyLoop = $manyLoop;
return $this;
}
/**
* Get manyLoop
*
* #return boolean
*/
public function getManyLoop()
{
return $this->manyLoop;
}
/**
* Set textEmail
*
* #param string $textEmail
*
* #return Project
*/
public function setTextEmail($textEmail)
{
$this->textEmail = $textEmail;
return $this;
}
/**
* Get textEmail
*
* #return string
*/
public function getTextEmail()
{
return $this->textEmail;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return Project
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return Project
*/
public function setUser(\AppBundle\Entity\User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Add participant
*
* #param \AppBundle\Entity\Participant $participant
*
* #return Project
*/
public function addParticipant(\AppBundle\Entity\Participant $participant)
{
$this->participants[] = $participant;
// On lie l'annonce à la candidature
$participant->setProject($this);
return $this;
}
/**
* Remove participant
*
* #param \AppBundle\Entity\Participant $participant
*/
public function removeParticipant(\AppBundle\Entity\Participant $participant)
{
$this->participants->removeElement($participant);
}
/**
* Get participants
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getParticipants()
{
return $this->participants;
}}
My Form:
$builder ->add('participants', ParticipantType::class);
/**
* {#inheritdoc}
*/
public function configureOptions (OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Project',
'step' => 1,
));
}
Form imbricated :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('lastName', TextType::class, array(
'required' => true,
'label' => 'Nom'
))
->add('firstName', TextType::class, array(
'required' => true,
'label' => 'Prénom'
))
->add('email', EmailType::class, array(
'required' => true,
'label' => 'Adresse mail'
));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('registration')
));
}
So here is to summarize my problem, I do not understand the error that Symfony returns, yet from the examples on the internet I do not see why there is a problem here ...
If someone to a solution would be top, because I blocked on this problem for a long time ...
Thank's !
Make sure that you don't forget to add : use Doctrine\Common\Collections\ArrayCollection or use $this->participants = new \Doctrine\Common\Collections\ArrayCollection();
In your FormType you should configure the participants field as a collection of Participant. It should be done using CollectionType like this:
$builder->add('participants', CollectionType::class, array(
'entry_type' => ParticipantType::class,
'allow_add' => true,
...
));
EDIT
According to your data model, the participants Field is a collection of Participant object. As you can see in the error message you presented, an array is expected. The CollectionType will always return an array of the target type.
That is why I suggest you to consider using a CollectionType in your form type. You can learn more about CollectionType here and about Symfony forms in general here

Symfony 3 form collection Call to a member function add() on a non-object

i'm trying to understand how to work with form collections in sf 3 but i'm having trouble following the tutorial from sf website because it hits me with an error and i kind manage understanding what i'm doing wrong and i hope you can help me out.
Offer Entity
<?php
namespace Zenith\OfferGeneratorBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use Zenith\productsBundle\Entity\products;
/**
* Offer
*
* #ORM\Table(name="offer")
* #ORM\Entity(repositoryClass="Zenith\OfferGeneratorBundle\Repository\OfferRepository")
*/
class Offer
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\ManyToMany(targetEntity="ProductCollection", inversedBy="offer", cascade={"persist"})
*/
private $products;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="createdAt", type="datetime")
*/
private $createdAt;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modifiedAt", type="datetime")
*/
private $modifiedAt;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Offer
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set createdAt
*
* #param \DateTime $createdAt
*
* #return Offer
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set modifiedAt
*
* #param \DateTime $modifiedAt
*
* #return Offer
*/
public function setModifiedAt($modifiedAt)
{
$this->modifiedAt = $modifiedAt;
return $this;
}
/**
* Get modifiedAt
*
* #return \DateTime
*/
public function getModifiedAt()
{
return $this->modifiedAt;
}
/**
* Add product
*
* #param \Zenith\OfferGeneratorBundle\Entity\ProductCollection $product
*
* #return Offer
*/
public function addProduct(ProductCollection $product)
{
$product->addOffer($this);
$this->products->add($product);
}
/**
* Remove product
*
* #param \Zenith\OfferGeneratorBundle\Entity\ProductCollection $product
*/
public function removeProduct(\Zenith\OfferGeneratorBundle\Entity\ProductCollection $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
ProductCollection Entity
<?php
namespace Zenith\OfferGeneratorBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Product
*
* #ORM\Table( )
* #ORM\Entity(repositoryClass="Zenith\OfferGeneratorBundle\Repository\ProductRepository")
*/
class ProductCollection
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var int
*
* #ORM\Column(name="price", type="integer")
*/
private $price;
/**
* #var int
*
* #ORM\Column(name="quantity", type="integer")
*/
private $quantity;
/**
* #var string
*
* #ORM\ManyToMany(targetEntity="Offer", mappedBy="products")
*/
private $offer;
/**
* #var int
*
* #ORM\ManyToMany(targetEntity="Zenith\ProductBundle\Entity\Product")
*/
private $product;
/**
* Constructor
*/
public function __construct()
{
$this->offer = new \Doctrine\Common\Collections\ArrayCollection();
$this->product = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return ProductCollection
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set price
*
* #param integer $price
*
* #return ProductCollection
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* #return integer
*/
public function getPrice()
{
return $this->price;
}
/**
* Set quantity
*
* #param integer $quantity
*
* #return ProductCollection
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
/**
* Get quantity
*
* #return integer
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* Add offer
*
* #param \Zenith\OfferGeneratorBundle\Entity\Offer $offer
*
* #return ProductCollection
*/
public function addOffer(\Zenith\OfferGeneratorBundle\Entity\Offer $offer)
{
if (!$this->offer->contains($offer)) {
$this->offer->add($offer);
}
}
/**
* Remove offer
*
* #param \Zenith\OfferGeneratorBundle\Entity\Offer $offer
*/
public function removeOffer(\Zenith\OfferGeneratorBundle\Entity\Offer $offer)
{
$this->offer->removeElement($offer);
}
/**
* Get offer
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getOffer()
{
return $this->offer;
}
/**
* Add product
*
* #param \Zenith\ProductBundle\Entity\Product $product
*
* #return ProductCollection
*/
public function addProduct(\Zenith\ProductBundle\Entity\Product $product)
{
$this->product[] = $product;
return $this;
}
/**
* Remove product
*
* #param \Zenith\ProductBundle\Entity\Product $product
*/
public function removeProduct(\Zenith\ProductBundle\Entity\Product $product)
{
$this->product->removeElement($product);
}
/**
* Get product
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProduct()
{
return $this->product;
}
}
When i try to add something using the form after the submit i get the following error.
Error: Call to a member function add() on a non-object
500 Internal Server Error - FatalErrorException
Stack Trace:
{
$product->addOffer($this);
$this->products->add($product);
}
/**
Based on one of the answers from here i changed the code and i got another error that i think it's from data transformer that i'm using on one of the fields.
<?php
namespace Zenith\OfferGeneratorBundle\Form\DataTransformer;
use Zenith\ProductBundle\Entity\Product;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class ProductNameTransformer implements DataTransformerInterface
{
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* Transforms an object (issue) to a string (number).
*
* #param Issue|null $issue
* #return string
*/
public function transform($issue)
{
if (null === $issue) {
return '';
}
return $issue->getId();
}
/**
* Transforms a string (number) to an object (issue).
*
* #param string $issueNumber
* #return Issue|null
* #throws TransformationFailedException if object (issue) is not found.
*/
public function reverseTransform($issueNumber)
{
// no issue number? It's optional, so that's ok
if (!$issueNumber) {
return;
}
$issue = $this->manager
->getRepository('ProductBundle:Product')
// query for the issue with this id
->findByName($issueNumber)
;
if (null === $issue) {
// causes a validation error
// this message is not shown to the user
// see the invalid_message option
throw new TransformationFailedException(sprintf(
'An issue with number "%s" does not exist!',
$issueNumber
));
}
return $issue;
}
}
Notice: Array to string conversion
500 Internal Server Error - ContextErrorException
Stack Trace
in vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php at line 67 -
public function bindValue($param, $value, $type = \PDO::PARAM_STR)
{
try {
return parent::bindValue($param, $value, $type);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
at ErrorHandler ->handleError ('8', 'Array to string conversion', '/var/www/f.dev/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php', '67', array('param' => '1', 'value' => array(object(Product)), 'type' => '2'))
at PDOStatement ->bindValue ('1', array(object(Product)), '2')
in vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php at line 67 +
at PDOStatement ->bindValue ('1', array(object(Product)), '2')
in vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php at line 120 +
at Statement ->bindValue ('1', array(object(Product)), 'string')
in vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php at line 277 +
at BasicEntityPersister ->executeInserts ()
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 1018 +
at UnitOfWork ->executeInserts (object(ClassMetadata))
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 378 +
at UnitOfWork ->commit (null)
in vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php at line 356 +
at EntityManager ->flush ()
in src/Zenith/OfferGeneratorBundle/Controller/OfferController.php at line 118 +
at OfferController ->newOfferSubmitedAction (object(Request))
at call_user_func_array (array(object(OfferController), 'newOfferSubmitedAction'), array(object(Request)))
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php at line 139 +
at HttpKernel ->handleRaw (object(Request), '1')
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php at line 62 +
at HttpKernel ->handle (object(Request), '1', true)
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php at line 169 +
at Kernel ->handle (object(Request))
in web/app_dev.php at line 30 +
You need to initialise your products collection in your __construct, otherwise you are attempting to call add on a null value (products will be initialised as null).
public function __construct()
{
$this->products = new ArrayCollection();
}

Doctrine: ManyToMany matching criteria set wrong expressions

I have two entities: User and Event, they are in ManyToMany relationship.
I try to match events by criteria from $user->getEvents() but get nothing. After checking profiler i saw that criteria did not work correctly while build sql, i used expression lte and gte but in sql doctrine continue to use =.
Here are my classes.
User:
<?php
namespace App\UserBundle\Entity;
use App\CoreBundle\Entity\Event;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use JMS\Serializer\Annotation as JMS;
/**
* #JMS\ExclusionPolicy("all")
*
* #ORM\Entity(repositoryClass="App\UserBundle\Repository\UserRepository")
* #ORM\Table(name="users", uniqueConstraints={#ORM\UniqueConstraint(name="User",columns={"login", "email"})})
* #UniqueEntity(fields={"login","email"})
* #ORM\HasLifecycleCallbacks()
*/
class User
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="login", type="string", length=255, unique=true, nullable=false)
* #Assert\NotBlank()
*/
protected $login;
/**
* #JMS\Expose
*
* #ORM\Column(name="name", type="string", length=255, unique=true, nullable=false)
* #Assert\NotBlank()
*/
protected $name;
/**
* #JMS\Expose
*
* #ORM\Column(name="email", type="string", length=255, unique=true, nullable=false)
* #Assert\NotBlank()
*/
protected $email;
/**
* #ORM\Column(name="roles", type="array", nullable=false)
* #Assert\NotBlank()
*/
protected $roles;
/**
* #ORM\Column(name="blocked", type="boolean")
* #Assert\NotBlank()
*/
protected $blocked;
/**
* #ORM\Column(type="datetime")
*/
protected $created;
/**
* #ORM\Column(type="datetime")
*/
protected $updated;
/**
* #ORM\ManyToMany(targetEntity="App\CoreBundle\Entity\Event", inversedBy="users", cascade={"persist", "remove"})
* #ORM\JoinTable(name="users_events",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="cascade")},
* inverseJoinColumns={#ORM\JoinColumn(name="event_id", referencedColumnName="id", onDelete="cascade")}
* )
*/
protected $events;
public function __construct()
{
$this->roles = array();
$this->events = new ArrayCollection();
$this->setCreated(new \DateTime());
$this->setUpdated(new \DateTime());
}
public function __toString()
{
return (string) $this->id;
}
/**
* #ORM\PreUpdate
*/
public function setUpdatedValue()
{
$this->setUpdated(new \DateTime());
}
/**
* Set created.
*
* #param \DateTime $created
*
* #return User
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created.
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated.
*
* #param \DateTime $updated
*
* #return User
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated.
*
* #return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
public function getId()
{
return $this->id;
}
/**
* Get Login.
*
* #return string
*/
public function getLogin()
{
return $this->login;
}
/**
* Set Login.
*
* #param $login
*
* #return User
*/
public function setLogin($login)
{
$this->login = $login;
return $this;
}
/**
* Get Name.
*
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* Set Name.
*
* #param string $name
*
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get Email.
*
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* Set Email.
*
* #param mixed $email
*
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get roles.
*
* #return mixed
*/
public function getRoles()
{
return $this->roles;
}
/**
* Set Roles.
*
* #param mixed $roles
*
* #return User
*/
public function setRoles($roles)
{
$this->roles = array();
foreach ($roles as $role) {
$this->addRole($role);
}
return $this;
}
/**
* Add role to collection.
*
* #param $role
*
* #return User
*/
public function addRole($role)
{
$role = strtoupper($role);
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
return $this;
}
/**
* Get Blocked.
*
* #return boolean
*/
public function getBlocked()
{
return $this->blocked;
}
/**
* Set Blocked.
*
* #param boolean $blocked
*
* #return User
*/
public function setBlocked($blocked)
{
$this->blocked = $blocked;
return $this;
}
/**
* Add event.
*
* #param Event $event
*
* #return User
*/
public function addEvent(Event $event)
{
if (!$this->events->contains($event)) {
$this->events[] = $event;
}
return $this;
}
/**
* Remove event.
*
* #param Event $event
*
* #return User
*/
public function removeEvent(Event $event)
{
if ($this->events->contains($event)) {
$this->events->removeElement($event);
}
return $this;
}
/**
* Get events.
*
* #return ArrayCollection
*/
public function getEvents()
{
return $this->events;
}
}
Event:
<?php
namespace App\CoreBundle\Entity;
use App\UserBundle\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Event
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="App\CoreBundle\Repository\EventRepository")
*/
class Event
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="event_name", type="string", length=50)
*/
private $eventName;
/**
* #var string
*
* #ORM\Column(name="event_description", type="string", length=160)
*/
private $eventDescription;
/**
* #var string
*
* #ORM\Column(name="event_type", type="string", length=100)
*/
private $eventType;
/**
* #var boolean
*
* #ORM\Column(name="visible", type="boolean")
*/
private $visible;
/**
* #var \DateTime
*
* #ORM\Column(name="start_date", type="datetime")
*/
private $startDate;
/**
* #var \DateTime
*
* #ORM\Column(name="end_date", type="datetime")
*/
private $endDate;
/**
* #ORM\ManyToMany(targetEntity="App\UserBundle\Entity\User", mappedBy="events")
*/
protected $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set eventName
*
* #param string $eventName
* #return Event
*/
public function setEventName($eventName)
{
$this->eventName = $eventName;
return $this;
}
/**
* Get eventName
*
* #return string
*/
public function getEventName()
{
return $this->eventName;
}
/**
* Set eventDescription
*
* #param string $eventDescription
* #return Event
*/
public function setEventDescription($eventDescription)
{
$this->eventDescription = $eventDescription;
return $this;
}
/**
* Get eventDescription
*
* #return string
*/
public function getEventDescription()
{
return $this->eventDescription;
}
/**
* Set eventType
*
* #param string $eventType
* #return Event
*/
public function setEventType($eventType)
{
$this->eventType = $eventType;
return $this;
}
/**
* Get eventType
*
* #return string
*/
public function getEventType()
{
return $this->eventType;
}
/**
* Set visible
*
* #param boolean $visible
* #return Event
*/
public function setVisible($visible)
{
$this->visible = $visible;
return $this;
}
/**
* Get visible
*
* #return boolean
*/
public function getVisible()
{
return $this->visible;
}
/**
* Set startDate
*
* #param \DateTime $startDate
* #return Event
*/
public function setStartDate($startDate)
{
$this->startDate = $startDate;
return $this;
}
/**
* Get startDate
*
* #return \DateTime
*/
public function getStartDate()
{
return $this->startDate;
}
/**
* Set endDate
*
* #param \DateTime $endDate
* #return Event
*/
public function setEndDate($endDate)
{
$this->endDate = $endDate;
return $this;
}
/**
* Get endData
*
* #return \DateTime
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* Set users.
*
* #param ArrayCollection $users
*
* #return Event
*/
public function setUsers(ArrayCollection $users)
{
$this->users = $users;
return $this;
}
/**
* Add user.
*
* #param User $user
*
* #return Event
*/
public function addUser(User $user)
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
}
return $this;
}
/**
* Get user.
*
* #return User
*/
public function getUsers()
{
return $this->users;
}
/**
* Remove user
*
* #param User $user
*/
public function removeUser(User $user)
{
$this->users->removeElement($user);
}
}
And here is how i try to find events related to user:
private function getActiveEventsCriteria($camelStyle = true, $dateAsObject = true)
{
$currentDateTime = new \DateTime();
$criteria = Criteria::create()
->where(
Criteria::expr()->andX(
Criteria::expr()->lte($camelStyle ? 'startDate' : 'start_date', $dateAsObject ? $currentDateTime : $currentDateTime->format("Y-m-d H:i:s")),
Criteria::expr()->gte($camelStyle ? 'endDate' : 'end_date', $dateAsObject ? $currentDateTime : $currentDateTime->format("Y-m-d H:i:s"))
)
)
->andWhere(Criteria::expr()->eq('visible', 1));
return $criteria;
}
public function getParticipatingEventsBy(User $user)
{
$userEvents = $user->getEvents();
if (is_null($userEvents)) return [];
return $userEvents->matching($this->getActiveEventsCriteria(false, false));
}
$this->getParticipatingEventsBy($user);
In profiler/debug i see next sql:
SELECT
te.id AS id,
te.event_name AS event_name,
te.event_description AS event_description,
te.event_type AS event_type,
te.visible AS visible,
te.start_date AS start_date,
te.end_date AS end_date
FROM
event te
JOIN users_events t ON t.event_id = te.id
WHERE
t.user_id = ?
AND te.start_date = ?
AND te.end_date = ?
AND te.visible = ?
And with applied parameters:
SELECT
te.id AS id,
te.event_name AS event_name,
te.event_description AS event_description,
te.event_type AS event_type,
te.visible AS visible,
te.start_date AS start_date,
te.end_date AS end_date
FROM
event te
JOIN users_events t ON t.event_id = te.id
WHERE
t.user_id = 14 AND
te.start_date = '2015-12-03 16:26:44' AND
te.end_date = '2015-12-03 16:26:44' AND
te.visible = 1;
I changed WHERE to next:
WHERE
t.user_id = 14 AND
te.start_date <= '2015-12-03 16:26:44' AND
te.end_date >= '2015-12-03 16:26:44' AND
te.visible = 1;
And with that sql i got correct result.
When i use same criteria right with repository without relation to user - everything is ok:
public function getActiveEvents() {
return $this->matching($this->getActiveEventsCriteria());
}
public function matching(Criteria $criteria)
{
return $this->repository->matching($criteria);
}
$this->getActiveEvents();
This is formatted sql:
SELECT
t0.id AS id_1,
t0.event_name AS event_name_2,
t0.event_description AS event_description_3,
t0.event_type AS event_type_4,
t0.visible AS visible_5,
t0.start_date AS start_date_6,
t0.end_date AS end_date_7
FROM
event t0
WHERE
(
(
t0.start_date <= ?
AND t0.end_date >= ?
)
AND t0.visible = ?
)
And with applied parameters:
SELECT
t0.id AS id_1,
t0.event_name AS event_name_2,
t0.event_description AS event_description_3,
t0.event_type AS event_type_4,
t0.visible AS visible_5,
t0.start_date AS start_date_6,
t0.end_date AS end_date_7
FROM
event t0
WHERE
((t0.start_date <= '2015-12-03 17:01:47' AND t0.end_date >= '2015-12-03 17:01:47') AND
t0.visible = 1);
As you can see, for case when i dont try to search through relation - everything is ok, i get result. But when i try related events to user with use of Criteria - it fails and return empty result cause of incorrect sql.
I use php 5.6.*, symfony 2.8, doctrine 2.5.2, latest MySQL.
Any help will be very appreciated.
Thank you.
UPDATE:
- created issue in JIRA: here

Form nulling a value when user doesn't supply it

Reading this page I've setup a form to handle PATCH requests.
I've a Player entity:
<?php
namespace Acme\PlayerBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\PlayerBundle\Entity\Player
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\PlayerBundle\Entity\PlayerRepository")
*/
class Player
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="players")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #ORM\ManyToOne(targetEntity="Acme\TeamBundle\Entity\Team", inversedBy="players")
* #ORM\JoinColumn(nullable=false)
* #Assert\NotBlank()
*/
private $team;
/**
* #var integer $shirtNumber
*
* #ORM\Column(name="shirtNumber", type="smallint")
* #Assert\NotBlank()
*/
private $shirtNumber;
/**
* #var integer $vsid
*
* #ORM\Column(name="vsid", type="integer", nullable=true)
*/
private $vsid;
/**
* #var string $firstname
*
* #ORM\Column(name="firstname", type="string", length=255, nullable=true)
*/
private $firstname;
/**
* #var string $lastname
*
* #ORM\Column(name="lastname", type="string", length=255, nullable=true)
*/
private $lastname;
/**
* #var boolean $deleted
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted = false;
/**
* #var integer $role
*
* #ORM\Column(type="integer", nullable=true)
*/
private $role;
/**
* Create the user salt
*/
public function __construct()
{
//TODO: just for test
$this->uniqueId = substr(uniqid(), 0, 14);
}
/* MANAGED BY DOCTRINE, DON'T EDIT */
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Player
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set shirtNumber
*
* #param integer $shirtNumber
* #return Player
*/
public function setShirtNumber($shirtNumber)
{
$this->shirtNumber = $shirtNumber;
return $this;
}
/**
* Get shirtNumber
*
* #return integer
*/
public function getShirtNumber()
{
return $this->shirtNumber;
}
/**
* Set vsid
*
* #param integer $vsid
* #return Player
*/
public function setVsid($vsid)
{
$this->vsid = $vsid;
return $this;
}
/**
* Get vsid
*
* #return integer
*/
public function getVsid()
{
return $this->vsid;
}
/**
* Set firstname
*
* #param string $firstname
* #return Player
*/
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
/**
* Get firstname
*
* #return string
*/
public function getFirstname()
{
return $this->firstname;
}
/**
* Set lastname
*
* #param string $lastname
* #return Player
*/
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
/**
* Get lastname
*
* #return string
*/
public function getLastname()
{
return $this->lastname;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Player
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Set role
*
* #param integer $role
* #return Player
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role
*
* #return integer
*/
public function getRole()
{
return $this->role;
}
/**
* Set owner
*
* #param Acme\UserBundle\Entity\User $owner
* #return Player
*/
public function setOwner(\Acme\UserBundle\Entity\User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
/**
* Set team
*
* #param Acme\TeamBundle\Entity\Team $team
* #return Player
*/
public function setTeam(\Acme\TeamBundle\Entity\Team $team)
{
$this->team = $team;
return $this;
}
/**
* Get team
*
* #return Acme\TeamBundle\Entity\Team
*/
public function getTeam()
{
return $this->team;
}
}
and a Team entity:
<?php
namespace Acme\TeamBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\TeamBundle\Entity\Team
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\TeamBundle\Entity\TeamRepository")
*/
class Team
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $uniqueId
*
* #ORM\Column(name="uniqueId", type="string", length=15)
*/
private $uniqueId;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="teams")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=50)
* #Assert\NotBlank()
*/
private $name;
/**
* #var string $homeColor
*
* #ORM\Column(name="homeColor", type="string", length=7, nullable=true)
*/
private $homeColor;
/**
* #var string $awayColor
*
* #ORM\Column(name="awayColor", type="string", length=7, nullable=true)
*/
private $awayColor;
/**
* #var string $homeShirt
*
* #ORM\Column(name="homeShirt", type="string", length=50, nullable=true)
*/
private $homeShirt;
/**
* #var string $awayShirt
*
* #ORM\Column(name="awayShirt", type="string", length=50, nullable=true)
*/
private $awayShirt;
/**
* #var string $teamLogo
*
* #ORM\Column(name="teamLogo", type="string", length=50, nullable=true)
*/
private $teamLogo;
/**
* #var boolean $deleted
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted = false;
/**
* #ORM\OneToMany(targetEntity="Acme\PlayerBundle\Entity\Player", mappedBy="team", cascade={"persist", "remove"})
*/
private $players;
/**
* #ORM\OneToMany(targetEntity="Acme\MatchBundle\Entity\Match", mappedBy="homeTeam")
*/
private $homeMatches;
/**
* #ORM\OneToMany(targetEntity="Acme\MatchBundle\Entity\Match", mappedBy="awayTeam")
*/
private $awayMatches;
/**
* Create the user salt
*/
public function __construct()
{
$this->players = new ArrayCollection();
$this->homeMatches = new ArrayCollection();
$this->awayMatches = new ArrayCollection();
//TODO: just for test
$this->uniqueId = substr(uniqid(), 0, 14);
}
public function getMatches()
{
return array_merge($this->awayMatches->toArray(), $this->homeMatches->toArray());
}
/* MANAGED BY DOCTRINE, DON'T EDIT */
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set uniqueId
*
* #param string $uniqueId
* #return Team
*/
public function setUniqueId($uniqueId)
{
$this->uniqueId = $uniqueId;
return $this;
}
/**
* Get uniqueId
*
* #return string
*/
public function getUniqueId()
{
return $this->uniqueId;
}
/**
* Set name
*
* #param string $name
* #return Team
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set homeColor
*
* #param string $homeColor
* #return Team
*/
public function setHomeColor($homeColor)
{
$this->homeColor = $homeColor;
return $this;
}
/**
* Get homeColor
*
* #return string
*/
public function getHomeColor()
{
return $this->homeColor;
}
/**
* Set awayColor
*
* #param string $awayColor
* #return Team
*/
public function setAwayColor($awayColor)
{
$this->awayColor = $awayColor;
return $this;
}
/**
* Get awayColor
*
* #return string
*/
public function getAwayColor()
{
return $this->awayColor;
}
/**
* Set homeShirt
*
* #param string $homeShirt
* #return Team
*/
public function setHomeShirt($homeShirt)
{
$this->homeShirt = $homeShirt;
return $this;
}
/**
* Get homeShirt
*
* #return string
*/
public function getHomeShirt()
{
return $this->homeShirt;
}
/**
* Set awayShirt
*
* #param string $awayShirt
* #return Team
*/
public function setAwayShirt($awayShirt)
{
$this->awayShirt = $awayShirt;
return $this;
}
/**
* Get awayShirt
*
* #return string
*/
public function getAwayShirt()
{
return $this->awayShirt;
}
/**
* Set teamLogo
*
* #param string $teamLogo
* #return Team
*/
public function setTeamLogo($teamLogo)
{
$this->teamLogo = $teamLogo;
return $this;
}
/**
* Get teamLogo
*
* #return string
*/
public function getTeamLogo()
{
return $this->teamLogo;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Team
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Add players
*
* #param Acme\PlayerBundle\Entity\Player $players
* #return Team
*/
public function addPlayer(\Acme\PlayerBundle\Entity\Player $players)
{
$this->players[] = $players;
return $this;
}
/**
* Remove players
*
* #param Acme\PlayerBundle\Entity\Player $players
*/
public function removePlayer(\Acme\PlayerBundle\Entity\Player $players)
{
$this->players->removeElement($players);
}
/**
* Get players
*
* #return Doctrine\Common\Collections\Collection
*/
public function getPlayers()
{
return $this->players;
}
/**
* Add homeMatches
*
* #param Acme\MatchBundle\Entity\Match $homeMatches
* #return Team
*/
public function addHomeMatche(\Acme\MatchBundle\Entity\Match $homeMatches)
{
$this->homeMatches[] = $homeMatches;
return $this;
}
/**
* Remove homeMatches
*
* #param Acme\MatchBundle\Entity\Match $homeMatches
*/
public function removeHomeMatche(\Acme\MatchBundle\Entity\Match $homeMatches)
{
$this->homeMatches->removeElement($homeMatches);
}
/**
* Get homeMatches
*
* #return Doctrine\Common\Collections\Collection
*/
public function getHomeMatches()
{
return $this->homeMatches;
}
/**
* Add awayMatches
*
* #param Acme\MatchBundle\Entity\Match $awayMatches
* #return Team
*/
public function addAwayMatche(\Acme\MatchBundle\Entity\Match $awayMatches)
{
$this->awayMatches[] = $awayMatches;
return $this;
}
/**
* Remove awayMatches
*
* #param Acme\MatchBundle\Entity\Match $awayMatches
*/
public function removeAwayMatche(\Acme\MatchBundle\Entity\Match $awayMatches)
{
$this->awayMatches->removeElement($awayMatches);
}
/**
* Get awayMatches
*
* #return Doctrine\Common\Collections\Collection
*/
public function getAwayMatches()
{
return $this->awayMatches;
}
/**
* Set owner
*
* #param Acme\UserBundle\Entity\User $owner
* #return Team
*/
public function setOwner(\Acme\UserBundle\Entity\User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
}
Now I've created a player form class with the app/console and I've edited the team field to be an instance of the Team entity, this way:
<?php
namespace Acme\PlayerBundle\Form;
use Acme\TeamBundle\Entity\TeamRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PlayerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('shirtNumber')
->add('firstname')
->add('lastname')
->add('role')
->add('team', 'entity', array(
'class' => 'AcmeTeamBundle:Team',
'query_builder' => function(TeamRepository $er) {
$query = $er->createQueryBuilder('t');
return $query;
}
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\PlayerBundle\Entity\Player',
'csrf_protection' => false
));
}
public function getName()
{
return 'player';
}
}
And this is the relevant part of my controller:
/**
* Create a new player
*
* #Route(".{_format}", name="api_player_create")
* #Method("POST")
* #ApiDoc(
* description="Create a new player",
* statusCodes={
* 201="Player created and informations are returned",
* 400="Missing informations",
* 403="The user isn't authorized"
* },
* input="Acme\PlayerBundle\Form\PlayerType",
* return="Acme\PlayerBundle\Entity\Player"
* )
*
* #return Renders the player just created
*/
public function createPlayerAction()
{
return $this->processForm(new Player());
}
/**
* Edit a player
*
* #param integer $id The id of the player to be created
*
* #Route("/{id}.{_format}", name="api_player_patch", requirements={ "id": "\d+" })
* #Method("PATCH")
* #ApiDoc(
* description="Edit a player",
* statusCodes={
* 200="Player is updated",
* 400="Missing informations",
* 403="The user isn't authorized"
* },
* input="Acme\PlayerBundle\Form\PlayerType",
* return="Acme\PlayerBundle\Entity\Player"
* )
*
* #return Renders the player just edited
*/
public function editPlayerAction(Player $player)
{
if ($player->getOwner() != $this->getUser()) {
throw new ApiException\PermissionDeniedException;
}
return $this->processForm($player);
}
/**
* Function to handle a form to create/edit a player
*
* #param Player $player The player to be created or edited
*
* #return Api Response
*/
private function processForm(Player $player)
{
/**
* Check if the player is new (to be created) or we're editing a player
*/
$statusCode = is_null($player->getId()) ? 201 : 200;
$form = $this->createForm(new PlayerType(), $player);
$form->bind($this->getRequest());
if ($form->isValid()) {
if($player->getTeam()->getOwner() != $this->getUser()) {
throw new ApiException\PermissionDeniedException;
}
$player->setOwner($this->getUser());
$this->entityManager->persist($player);
$this->entityManager->flush();
return $this->apiResponse->getResponse($player, $statusCode);
}
return $this->apiResponse->getResponse($form, 400, 'Missing arguments');
}
The player creation works fine, the player edit doesn't, when the user makes the api request, passing the ID in the url and the name of the player I get:
Catchable Fatal Error: Argument 1 passed to Acme\PlayerBundle\Entity\Player::setTeam() must be an instance of Acme\TeamBundle\Entity\Team, null given, called in /Volumes/Dati/Users/alessandro/Sites/acme-api/vendor/symfony/symfony/src/Symfony/Component/Form/Util/PropertyPath.php on line 538 and defined in /Volumes/Dati/Users/alessandro/Sites/acme-api/src/Acme/PlayerBundle/Entity/Player.php line 278
Seems that form is trying to set the Team to null, why?
I've tried both sending and not the team as the form parameters but it doesn't work.
Any clue?
Figured it out that null unsent fields in the form is the default behaviour in symfony.
There is an open request for partial form binding in symfony. For now a guy created a form event subscriber that adds the missing fields with the actual values:
https://gist.github.com/3720535
This is his code:
<?php
namespace Foo;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
/**
* Changes Form->bind() behavior so that it treats not set values as if they
* were sent unchanged.
*
* Use when you don't want fields to be set to NULL when they are not displayed
* on the page (or to implement PUT/PATCH requests).
*/
class PatchSubscriber implements EventSubscriberInterface
{
public function onPreBind(FormEvent $event)
{
$form = $event->getForm();
$clientData = $event->getData();
$clientData = array_replace($this->unbind($form), $clientData ?: array());
$event->setData($clientData);
}
/**
* Returns the form's data like $form->bind() expects it
*/
protected function unbind($form)
{
if ($form->hasChildren()) {
$ary = array();
foreach ($form->getChildren() as $name => $child) {
$ary[$name] = $this->unbind($child);
}
return $ary;
} else {
return $form->getClientData();
}
}
static public function getSubscribedEvents()
{
return array(
FormEvents::PRE_BIND => 'onPreBind',
);
}
}
to add to the form you have to edit the buildForm method in the form class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$subscriber = new PatchSubscriber();
$builder->addEventSubscriber($subscriber);
$builder->add('name');
....
}
In this case you're ok to use the PATCH REST requests to edit an entity just by the sent fields

Resources