doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.92k stars 2.51k forks source link

Not detected new mapped field #7881

Closed nmateo closed 4 years ago

nmateo commented 4 years ago

Bug Report

Q A
BC Break no
Version 2.4

Summary

I tried to map a field exactly as the same as one another in my entity, when running:

php bin/console doctrine:schema:update --dump-sql or php bin/console doctrine:schema:update --force i got this: [OK] Nothing to update - your database is already in sync with the current entity metadata. And the field is fine in the database: image

Te field is "promoted", same as "certified", same behavior expected.

Current behavior

In practice, whenever i try to work with the field "promoted" the same way as i do with the field "certified", it says there's no mapped field "promoted".

How to reproduce

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;

    /**
     * 
     *
     * @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;
    }
}`

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;
    }

}

Expected behavior

I excepted doctrine to query for "promoted" the same way as "certified", instead, it says it is'nt mapped, for more info, here's my post on stack overflow: https://stackoverflow.com/questions/58504171/why-isnt-my-property-mapped-to-the-entity

nmateo commented 4 years ago

Sorry for the code blocks, it doesnt work..

lcobucci commented 4 years ago

Did you perhaps have a typo in the version you're using? If not, is this reproducible on the latest version of the ORM (v2.6.4)? v2.4 is pretty much ancient and no longer maintained.

nmateo commented 4 years ago

Did you perhaps have a typo in the version you're using? If not, is this reproducible on the latest version of the ORM (v2.6.4)? v2.4 is pretty much ancient and no longer maintained.

hey thanks, i got this in my composer.lock: "doctrine/orm": "^2.5",

nmateo commented 4 years ago

The composer show command grep doctrine give me this:

image

nmateo commented 4 years ago

I also got this composer.json here in vendor/sonata-project/admin-bundle/composer.json:

{ "name": "sonata-project/admin-bundle", "type": "symfony-bundle", "description": "The missing Symfony Admin Generator", "keywords": [ "Admin Generator", "admin", "sonata", "bootstrap" ], "homepage": "https://sonata-project.org/bundles/admin", "license": "MIT", "authors": [ { "name": "Thomas Rabaix", "email": "thomas.rabaix@sonata-project.org", "homepage": "https://sonata-project.org" }, { "name": "Sonata Community", "homepage": "https://github.com/sonata-project/SonataAdminBundle/contributors" } ], "require": { "php": "^5.6 || ^7.0", "doctrine/common": "^2.7", "doctrine/inflector": "^1.1", "knplabs/knp-menu-bundle": "^2.2", "sonata-project/block-bundle": "^3.11", "sonata-project/core-bundle": "^3.9", "sonata-project/exporter": "^1.8", "symfony/asset": "^2.8 || ^3.2 || ^4.0", "symfony/config": "^2.8 || ^3.2 || ^4.0", "symfony/console": "^2.8 || ^3.2 || ^4.0", "symfony/dependency-injection": "^2.8 || ^3.2 || ^4.0", "symfony/event-dispatcher": "^2.8 || ^3.2 || ^4.0", "symfony/expression-language": "^2.8 || ^3.2 || ^4.0", "symfony/form": "^2.8 || ^3.2 || ^4.0", "symfony/framework-bundle": "^2.8 || ^3.2 || ^4.0", "symfony/http-foundation": "^2.8 || ^3.2 || ^4.0", "symfony/http-kernel": "^2.8 || ^3.2 || ^4.0", "symfony/options-resolver": "^2.8 || ^3.2 || ^4.0", "symfony/property-access": "^2.8 || ^3.2 || ^4.0", "symfony/routing": "^2.8 || ^3.2 || ^4.0", "symfony/security-acl": "^2.8 || ^3.0", "symfony/security-bundle": "^2.8 || ^3.2 || ^4.0", "symfony/security-core": "^2.8 || ^3.2 || ^4.0", "symfony/security-csrf": "^2.8 || ^3.2 || ^4.0", "symfony/templating": "^2.8 || ^3.2 || ^4.0", "symfony/translation": "^2.8 || ^3.2 || ^4.0", "symfony/twig-bridge": "^2.8 || ^3.2 || ^4.0", "symfony/twig-bundle": "^2.8 || ^3.2 || ^4.0", "symfony/validator": "^2.8 || ^3.2 || ^4.0", "twig/extensions": "^1.5", "twig/twig": "^1.34 || ^2.0" },"conflict": { "jms/di-extra-bundle": "<1.9", "sonata-project/media-bundle": "<3.7", "sonata-project/user-bundle": "<3.3" }, "require-dev": { "jms/di-extra-bundle": "^1.9", "jms/translation-bundle": "^1.4", "matthiasnoback/symfony-dependency-injection-test": "^1.1", "sensio/generator-bundle": "^3.1", "sonata-project/intl-bundle": "^2.4", "symfony/class-loader": "^2.8 || ^3.2", "symfony/filesystem": "^2.8 || ^3.2 || ^4.0", "symfony/phpunit-bridge": "^4.0", "symfony/yaml": "^2.8 || ^3.2 || ^4.0" }, "suggest": { "jms/di-extra-bundle": "Annotations for Admin definition", "jms/translation-bundle": "Extract message keys from Admins", "sensio/generator-bundle": "Add sonata:admin:generate command", "sonata-project/intl-bundle": "Add localized date and number into the list" }, "config": { "sort-packages": true }, "extra": { "branch-alias": { "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { "Sonata\AdminBundle\": "src/" } }, "autoload-dev": { "psr-4": { "Sonata\AdminBundle\Tests\": "tests/" } } }

nmateo commented 4 years ago

I think this is prob a problem of versioning/compatibillity right ?

Ocramius commented 4 years ago

No, probably something else. Have you tried reducing this to just the affected fields? What happens if you run orm:info with this entity and just the identifier plus the two boolean fields?

lcobucci commented 4 years ago

composer show gives version 2.6.2 for this package (doctrine/orm) :+1: Your mapping seems okay (a bit convoluted, though).

I'd suggest updating to the latest ORM v2.6.4 (just to ensure you have the latest bugfixes - none related to this specifically) and to isolate the ORM behaviour from the rest - you have many things being integrated (various traits and usage of SF forms).

That isolation could be done by instantiating the object, persisting it (persist + flush), and fetching it from the DB (clear + find). This process would show if the ORM is the culprit here.

nmateo commented 4 years ago

Updated to 2.6.4 Thanks @lcobucci. Could you show me a little bit how to instantiating the object, persisting it (persist + flush), and fetching it from the DB (clear + find) ?

nmateo commented 4 years ago

Thanks @Ocramius. The php bin/console orm:info gives me nothing but an undefined command.

Ocramius commented 4 years ago

Maybe doctrine:info? No idea how this is done in symfony :-P

nmateo commented 4 years ago

Thanks Ocramius, got this: image

Ocramius commented 4 years ago

Maybe orm:mapping:describe (doctrine:mapping:describe in symfony context?) will help further.

Closing here meanwhile: as mentioned above, you have to isolate the issue by reducing the code until the minimum reproducible scenario, without the rest of the application code.

Once it is clear if/that it is an ORM bug, then please re-open with a more reduced example, maybe to be added to https://github.com/doctrine/doctrine2/tree/master/tests/Doctrine/Tests/ORM/Functional/Ticket

nmateo commented 4 years ago

I can assure you it's a doctrine-orm issue since it tells me all is up to date and when i try to use my field suddenly it says it is not mapped, i manage to get a mapping description with: php bin/console sonata:core:dump-doctrine-metadata

image

Ocramius commented 4 years ago

And no caches involved?

lcobucci commented 4 years ago

And no caches involved?

Especially metadata cache.

nmateo commented 4 years ago

I cleared all caches including doctrine metadata multiples times

nmateo commented 4 years ago

I got an error generating the dump of doctrine metadata here it is:

image

nmateo commented 4 years ago

Thanks for the help it's now resolved after upgrading some dependencies and clearing caches it works great thanks again for the help!