【问题标题】:Symfony 2 doctrine DQL order by two fieldsSymfony 2 学说 DQL 按两个字段排序
【发布时间】:2016-01-15 09:19:39
【问题描述】:

大家好,当我使用自定义 DQL 从存储库获取时,我有两个对象 Point 和 Subpoint。我想按字段顺序对点进行排序,将子点按字段排序。

这里是实体:

namespace George\ArchitectureBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;

/**
 * Point
 *
 * @ORM\Table()
 *    @ORM\Entity(repositoryClass="George\ArchitectureBundle\Entity\PointRepository")
 * @Vich\Uploadable
 */
class Point
{
use Translatable;
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var Object
 * @Gedmo\SortableGroup
 * @ORM\ManyToOne(targetEntity="George\ObjectsBundle\Entity\Object", inversedBy="architecturespoints")
 * @ORM\JoinColumn(name="object_id", referencedColumnName="id")
 */
private $object;

/**
 * @ORM\OneToMany(targetEntity="George\ArchitectureBundle\Entity\Subpoint", mappedBy="point")
 */
private $subpoints;


/**
 * @var integer
 * @Gedmo\SortablePosition
 * @ORM\Column(name="ord", type="integer")
 */
private $ord;

/**
 * @var \DateTime
 * @Gedmo\Timestampable(on="update")
 * @ORM\Column(name="updated", type="datetime")
 */
private $updated;

/**
 * @var \DateTime
 * @Gedmo\Timestampable(on="create")
 * @ORM\Column(name="created", type="datetime")
 */
private $created;

public function __construct()
{
    $this->subpoints = new ArrayCollection();
}
/**
 * Get id
 *
 * @return integer
 */
public function getId()
{
    return $this->id;
}

/**
 * @return Object
 */
public function getObject()
{
    return $this->object;
}

/**
 * @param Object $object
 */
public function setObject($object)
{
    $this->object = $object;
}
/**
 * @return int
 */
public function getOrd()
{
    return $this->ord;
}

/**
 * @param int $ord
 */
public function setOrd($ord)
{
    $this->ord = $ord;
}
/**
 * @return \DateTime
 */
public function getUpdated()
{
    return $this->updated;
}

/**
 * @return \DateTime
 */
public function getCreated()
{
    return $this->created;
}

/**
 * @return mixed
 */
public function getSubpoints()
{
    return $this->subpoints;
}

/**
 * NOTE: This is not a mapped field of entity metadata, just a simple property.
 *
 * @Vich\UploadableField(mapping="point_image", fileNameProperty="imageName")
 *
 * @var File
 */
private $imageFile;

/**
 * @ORM\Column(type="string", length=255, nullable=true)
 *
 * @var string
 */
private $imageName;

/**
 * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
 * of 'UploadedFile' is injected into this setter to trigger the  update. If this
 * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
 * must be able to accept an instance of 'File' as the bundle will inject one here
 * during Doctrine hydration.
 *
 * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
 */
public function setImageFile(File $image = null)
{
    $this->imageFile = $image;

    if ($image) {
        // It is required that at least one field changes if you are using doctrine
        // otherwise the event listeners won't be called and the file is lost
        // $this->setModefied(new \DateTime('now')) ;
    }
}

/**
 * @return File
 */
public function getImageFile()
{
    return $this->imageFile;
}

/**
 * @param string $imageName
 */
public function setImageName($imageName)
{
    $this->imageName = $imageName;
}

/**
 * @return string
 */
public function getImageName()
{
    return $this->imageName;
}
}

子点:

namespace George\ArchitectureBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;


 /**
 * Subpoint
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="George\ArchitectureBundle\Entity\SubpointRepository")
 * @Vich\Uploadable
 */
class Subpoint
{
use Translatable;

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var Points
 * @Gedmo\SortableGroup
 * @ORM\ManyToOne(targetEntity="George\ArchitectureBundle\Entity\Point", inversedBy="subpoints")
 */
private $point;

/**
 * @var integer
 * @Gedmo\SortablePosition
 * @ORM\Column(name="ord", type="integer")
 */
private $ord;



/**
 * @var \DateTime
 * @Gedmo\Timestampable(on="update")
 * @ORM\Column(name="updated", type="datetime")
 */
private $updated;


/**
 * @var \DateTime
 * @Gedmo\Timestampable(on="create")
 * @ORM\Column(name="created", type="datetime")
 */
private $created;

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

/**
 * @return Points
 */
public function getPoint()
{
    return $this->point;
}

/**
 * @param Points $point
 */
public function setPoint($point)
{
    $this->point = $point;
}
/**
 * NOTE: This is not a mapped field of entity metadata, just a simple property.
 *
 * @Vich\UploadableField(mapping="point_image", fileNameProperty="imageName")
 *
 * @var File
 */
private $imageFile;

/**
 * @ORM\Column(type="string", length=255, nullable=true)
 *
 * @var string
 */
private $imageName;


/**
 * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
 * of 'UploadedFile' is injected into this setter to trigger the  update. If this
 * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
 * must be able to accept an instance of 'File' as the bundle will inject one here
 * during Doctrine hydration.
 *
 * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
 */
public function setImageFile(File $image = null)
{
    $this->imageFile = $image;

    if ($image) {
        // It is required that at least one field changes if you are using doctrine
        // otherwise the event listeners won't be called and the file is lost
        // $this->setModefied(new \DateTime('now')) ;
    }
}

/**
 * @return File
 */
public function getImageFile()
{
    return $this->imageFile;
}

/**
 * @param string $imageName
 */
public function setImageName($imageName)
{
    $this->imageName = $imageName;
}

/**
 * @return string
 */
public function getImageName()
{
    return $this->imageName;
}
/**
 * @return \DateTime
 */
public function getUpdated()
{
    return $this->updated;
}

/**
 * @return \DateTime
 */
public function getCreated()
{
    return $this->created;
}

/**
 * @return int
 */
public function getOrd()
{
    return $this->ord;
}

/**
 * @param int $ord
 */
public function setOrd($ord)
{
    $this->ord = $ord;
}

}

存储库点,当我得到要由 ord 排序的点和要由 ord 排序的子点时,我想在这里:

namespace George\ArchitectureBundle\Entity;

/**
 * PointRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class PointRepository extends \Doctrine\ORM\EntityRepository
{
public function getPointsByObject($object)
{

    $em = $this->getEntityManager();
    $query = $em->createQuery("SELECT p  FROM George\ArchitectureBundle\Entity\Point p WHERE p.object =".$object." ORDER BY p.ord ASC");
    return  $query->getResult();


}
}

但是当我将 creatQuery 放入点存储库时

"SELECT p  FROM George\ArchitectureBundle\Entity\Point p WHERE p.object =".$object." ORDER BY p.ord ASC, p.subpoints.ord ASC "

我收到错误:

[Semantical Error] line 0, col 107 near 'ord ASC ': Error: Class George\ArchitectureBundle\Entity\Point has no field or association named subpoints.ord 

编辑 问题的解决方案是在@Yoshi 和@Veve 的指导下使用查询生成器:

public function getPointsByObject($object)
{

    $em = $this->getEntityManager();
  //  $query = $em->createQuery("SELECT p  FROM George\ArchitectureBundle\Entity\Point p left join George\ArchitectureBundle\Entity\Subpoint s WITH s.point = p  WHERE p.object =".$object." ORDER BY p.ord ASC, s.ord ASC");
    $qb = $em->createQueryBuilder();
    $qb->select('p')
        ->from('George\ArchitectureBundle\Entity\Point','p')
        ->where(' p.object =:object')
        ->leftJoin('George\ArchitectureBundle\Entity\Subpoint', 's', 'WITH', 's.point = p')
        ->orderBy('p.ord','ASC')
        ->orderBy('s.ord','ASC');

    $qb->setParameters(array(
        'object' => $object
    ));
    $query= $qb->getQuery();

    return  $query->getResult();


}

【问题讨论】:

  • 您有什么充分的理由使用教义并仍然手动构建查询字符串?这里有严重问题。使用setParameter 或类似名称。
  • 您对参数的看法是对的,但问题出在订单语句上,它在第二个订单中,而不是在参数对象中。
  • 我的意思是,当您正确构建查询时,您肯定会更轻松地解决订单问题。例如。使用查询生成器,您可以根据需要多次添加addOrderBy()。也就是说,也许添加 renderd dql,这样我们就可以看到它而无需猜测 $object 可能会做什么。
  • 你说得对,我会重建它并编辑帖子。
  • 但我可能对这个问题有所猜测。我认为您不能像您那样直接按关系订购。我认为您必须添加一个带有别名的连接,并将此别名 + .ord 用于第二个顺序子句。

标签: symfony doctrine-orm dql


【解决方案1】:

您必须加入子点才能按其属性之一排序:

"SELECT p FROM George\ArchitectureBundle\Entity\Point p
JOIN George\ArchitectureBundle\Entity\Subpoint s WITH s.point = p.id
WHERE p.object =".$object."
ORDER BY p.ord ASC, s.ord ASC"

作为Yoshicommented,您应该使用 queryBuilder 并使用它添加参数,而不是手动构建查询。

【讨论】:

  • 谢谢你我只需要用 WITH 替换
  • 哦,您不应该在构建查询时盲目使用变量(例如,您的应用程序可能容易受到 SQL 注入的攻击)。而是为它使用一个参数。
  • @xabbuh 您对 JOIN 的看法是正确的,当然,正如 Yoshi 所说,使用查询构建器会好得多。乔治同意。
  • @Veve 实际上我不会在这里使用查询生成器(查询本身不是变量,唯一变量是值,但始终使用)。
猜你喜欢
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多