I try to create a tree structure for a catalog.
First the user creates the catalog with basic informations (name, code, validity period, type, etc.). Then, I redirect user in another form to create the tree structure.
The catalog can have multiple levels.
The levels can contain multiple products (named here "Model").
So, in my tree form, I have a collection of levels.
And in the level collection form, I have a collection of models.
In other words, I have a collection into another collection.
I don't manage to save my several entities in database. It losts the model values (second collection) when I do $em->flush(); in my controller. I have an integrity constraint violation on the composite key of CatalogModel entity (which is composed of the level id and catalog code).
However, Catalog, CatalogLevel and CatalogModel are well persisted. There are both filled with correct datas.
So I don't understand what I'm doing wrong....
Catalog Entity : Note that I don't use an auto generated Id. The Code is the id, and it is provided by the user when he creates the catalog.
/**
* #ORM\Table(name="catalogue")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogRepository")
* #UniqueEntity(fields="code", message="Catalog code already exists")
*/
class Catalog
{
/**
* #ORM\Column(name="Catalogue_Code", type="string", length=15)
* #ORM\Id
* #Assert\NotBlank()
* #Assert\Length(max=15, maxMessage="The code is too long ({{ limit }} characters max)")
*/
private $code;
/**
* #ORM\OneToMany(targetEntity="CatalogLevel", mappedBy="catalog", cascade={"persist", "remove"})
* #Assert\Valid
*/
private $levels;
/**
* Constructor
*/
public function __construct()
{
$this->levels = new ArrayCollection();
}
/**
* Set code
*
* #param string $code
*
* #return Catalog
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Get levels
*
* #return ArrayCollection
*/
public function getLevels()
{
return $this->levels;
}
/**
* Add level
*
* #param \AppBundle\Entity\CatalogLevel $level
*
* #return Catalog
*/
public function addLevel(\AppBundle\Entity\CatalogLevel $level)
{
$level->setCatalogCode($this->getCode());
$level->setCatalog($this);
if (!$this->getLevels()->contains($level)) {
$this->levels->add($level);
}
return $this;
}
/**
* Remove level
*
* #param \AppBundle\Entity\CatalogLevel $level
*/
public function removeLevel(\AppBundle\Entity\CatalogLevel $level)
{
$this->levels->removeElement($level);
}
}
CatalogLevel entity : I don't use an auto-generated ID either. Level as a composite key composed with level id and catalog code. The id is generated in JS when I create a level with the add button in the form. Remember, levels are a collection form.
/**
* #ORM\Table(name="catalogue_niveau")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogLevelRepository")
*/
class CatalogLevel
{
/**
* #ORM\Column(name="Niveau_ID", type="string", length=15)
* #ORM\Id
*/
private $id;
/**
* #ORM\Column(name="Catalogue_Code", type="string", length=15)
* #ORM\Id
*/
private $catalogCode;
/**
* #ORM\Column(name="Niveau_Nom", type="string", length=40)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Column(name="Parent_ID", type="string", length=15, nullable=true)
* #ORM\ManyToOne(targetEntity="CatalogLevel")
* #ORM\JoinColumn(referencedColumnName="Niveau_ID", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\Column(name="Niveau_Ordre", type="integer")
*/
private $order;
/**
* #ORM\ManyToOne(targetEntity="Catalog", inversedBy="levels")
* #ORM\JoinColumn(name="Catalogue_Code", referencedColumnName="Catalogue_Code")
*/
private $catalog;
/**
* #ORM\OneToMany(targetEntity="CatalogModel", mappedBy="level", cascade={"persist", "remove"})
* #Assert\Valid
*/
private $models;
/**
* Constructor
*/
public function __construct()
{
$this->order = 0;
$this->models = new ArrayCollection();
}
/**
* Set id
*
* #param string $id
*
* #return CatalogLevel
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Set catalogCode
*
* #param string $catalogCode
*
* #return CatalogLevel
*/
public function setCatalogCode($catalogCode)
{
$this->catalogCode = $catalogCode;
return $this;
}
/**
* Get catalogCode
*
* #return string
*/
public function getCatalogCode()
{
return $this->catalogCode;
}
/**
* Set name
*
* #param string $name
*
* #return CatalogLevel
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set parent
*
* #param string $parent
*
* #return CatalogLevel
*/
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* #return string
*/
public function getParent()
{
return $this->parent;
}
/**
* Set order
*
* #param integer $order
*
* #return CatalogLevel
*/
public function setOrder($order)
{
$this->order = $order;
return $this;
}
/**
* Get order
*
* #return integer
*/
public function getOrder()
{
return $this->order;
}
/**
* Set catalog
*
* #param \AppBundle\Entity\Catalog $catalog
*
* #return CatalogLevel
*/
public function setCatalog(\AppBundle\Entity\Catalog $catalog = null)
{
$this->catalog = $catalog;
return $this;
}
/**
* Get catalog
*
* #return \AppBundle\Entity\Catalog
*/
public function getCatalog()
{
return $this->catalog;
}
/**
* Get models
*
* #return ArrayCollection
*/
public function getModels()
{
return $this->models;
}
/**
* Add model
*
* #param \AppBundle\Entity\CatalogModel $model
*
* #return CatalogLevel
*/
public function addModel(\AppBundle\Entity\CatalogModel $model)
{
$model->setLevel($this);
$model->setCatalog($this->getCatalog());
if (!$this->getModels()->contains($model)) {
$this->models->add($model);
}
return $this;
}
/**
* Remove model
*
* #param \AppBundle\Entity\CatalogModel $model
*/
public function removeModel(\AppBundle\Entity\CatalogModel $model)
{
$this->models->removeElement($model);
}
}
CatalogModel entity : model has a composite key too, composed with code (the code of the Model entity), catalog code and level id.
/**
* #ORM\Table(name="catalogue_rayon")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CatalogModelRepository")
*/
class CatalogModel
{
/**
* #ORM\Column(name="Modele_Code", type="string", length=20)
* #ORM\Id
*/
private $code;
/**
* #ORM\Column(name="Catalogue_Code", type="string", length=15)
* #ORM\Id
*/
private $catalogCode;
/**
* #ORM\Column(name="Niveau_ID", type="string", length=15)
* #ORM\Id
*/
private $levelId;
/**
* #ORM\Column(name="Modele_Ordre", type="integer")
*/
private $order;
/**
* #ORM\ManyToOne(targetEntity="CatalogLevel", inversedBy="models")
* #ORM\JoinColumn(name="Niveau_ID", referencedColumnName="Niveau_ID")
*/
private $level;
/**
* #ORM\ManyToOne(targetEntity="Catalog")
* #ORM\JoinColumn(name="Catalogue_Code", referencedColumnName="Catalogue_Code")
*/
private $catalog;
/**
* #ORM\ManyToOne(targetEntity="Model")
* #ORM\JoinColumn(name="Modele_Code", referencedColumnName="Modele_Code")
*/
private $model;
/**
* #ORM\Column(name="Date_Debut", type="datetime", nullable=true)
* #Assert\Date()
*/
private $beginDate;
/**
* #ORM\Column(name="Date_Fin", type="datetime", nullable=true)
* #Assert\Date()
*/
private $endDate;
/**
* Constructor
*/
public function __construct()
{
$this->order = 0;
}
/**
* Set code
*
* #param string $code
*
* #return CatalogModel
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set catalogCode
*
* #param string $catalogCode
*
* #return CatalogModel
*/
public function setCatalogCode($catalogCode)
{
$this->catalogCode = $catalogCode;
return $this;
}
/**
* Get catalogCode
*
* #return string
*/
public function getCatalogCode()
{
return $this->catalogCode;
}
/**
* Set levelId
*
* #param integer $levelId
*
* #return CatalogModel
*/
public function setLevelId($levelId)
{
$this->levelId = $levelId;
return $this;
}
/**
* Get levelId
*
* #return integer
*/
public function getLevelId()
{
return $this->levelId;
}
/**
* Set order
*
* #param integer $order
*
* #return CatalogModel
*/
public function setOrder($order)
{
$this->order = $order;
return $this;
}
/**
* Get order
*
* #return integer
*/
public function getOrder()
{
return $this->order;
}
/**
* Set level
*
* #param \AppBundle\Entity\CatalogLevel $level
*
* #return CatalogModel
*/
public function setLevel(\AppBundle\Entity\CatalogLevel $level = null)
{
$this->level = $level;
return $this;
}
/**
* Get level
*
* #return \AppBundle\Entity\CatalogLevel
*/
public function getLevel()
{
return $this->level;
}
/**
* Set catalog
*
* #param \AppBundle\Entity\Catalog $catalog
*
* #return CatalogModel
*/
public function setCatalog(\AppBundle\Entity\Catalog $catalog = null)
{
$this->catalog = $catalog;
return $this;
}
/**
* Get catalog
*
* #return \AppBundle\Entity\Catalog
*/
public function getCatalog()
{
return $this->catalog;
}
/**
* Set beginDate
*
* #param \DateTime $beginDate
*
* #return CatalogModel
*/
public function setBeginDate($beginDate)
{
$this->beginDate = $beginDate;
return $this;
}
/**
* Get beginDate
*
* #return \DateTime
*/
public function getBeginDate()
{
return $this->beginDate;
}
/**
* Set endDate
*
* #param \DateTime $endDate
*
* #return CatalogModel
*/
public function setEndDate($endDate)
{
$this->endDate = $endDate;
return $this;
}
/**
* Get endDate
*
* #return \DateTime
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* Set model
*
* #param \AppBundle\Entity\Model $model
*
* #return CatalogModel
*/
public function setModel(\AppBundle\Entity\Model $model = null)
{
$this->model = $model;
return $this;
}
/**
* Get model
*
* #return \AppBundle\Entity\Model
*/
public function getModel()
{
return $this->model;
}
}
Then, the forms :
CatalogTreeType
class CatalogTreeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('levels', CollectionType::class, array(
'entry_type' => CatalogLevelType::class,
'entry_options' => array('catalogCode' => $options['data']->getCode()),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'label' => false,
'by_reference' => false
))
->add('search', TextType::class, array(
'label' => false,
'mapped' => false,
'required' => false,
'attr' => array(
'class' => 'search',
'size' => 35,
'placeholder' => 'Search for a model...'
)
))
->add('save', SubmitType::class, array(
'attr' => array('class' => 'button-link save'),
'label' => 'Validate'
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Catalog',
// 'allow_extra_fields' => true
));
}
}
CatalogLevelType (collection)
class CatalogLevelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'label' => false,
'attr' => array(
'class' => 'full level-name',
'size' => 35,
'placeholder' => 'Name of level'
)
))
->add('id', HiddenType::class, array(
'attr' => array('class' => 'level-id')
))
->add('parent', HiddenType::class, array(
'attr' => array('class' => 'level-parent'),
'required' => false
))
->add('models', CollectionType::class, array(
'entry_type' => CatalogModelType::class,
'entry_options' => array('catalogCode' => $options['catalogCode']),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__model_name__',
'label' => false,
'by_reference' => false
))
;
}
CatalogModelType (collection)
class CatalogModelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code', TextType::class, array(
'label' => false,
'attr' => array(
'class' => 'full model-field',
'readonly' => true
)
))
->add('beginDate', DateType::class, array(
'label' => 'Begins',
'widget' => 'single_text',
'html5' => false,
'attr' => array('class' => 'full'),
'format' => 'yyyy-MM-dd',
'data' => new \DateTime('0000-00-00'),
'required' => false
))
->add('endDate', DateType::class, array(
'label' => 'Ends',
'widget' => 'single_text',
'html5' => false,
'attr' => array('class' => 'full'),
'format' => 'yyyy-MM-dd',
'data' => new \DateTime('0000-00-00'),
'required' => false
))
->add('catalogCode', HiddenType::class, array(
'data' => $options['catalogCode']
))
->add('levelId', HiddenType::class, array(
'attr' => array('class' => 'model-level-id')
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\CatalogModel',
'catalogCode' => null
));
}
}
And CatalogController
/**
* #Route("/catalogs/{code}/tree-structure", name="catalog_tree")
*/
public function treeAction($code, Request $request) {
// get catalog from database
$em = $this->getDoctrine()->getManager();
$catalog = $em->getRepository('AppBundle:Catalog')->find($code);
// catalog doesn't exist
if (!$catalog) {
throw $this->createNotFoundException('No catalog found for code '. $code);
}
// build the form
$form = $this->createForm(CatalogTreeType::class, $catalog);
// Form posté et valide
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($catalog); // save the catalog
// save the levels
foreach($catalog->getLevels() as $level) {
// var_dump($level);
$em->persist($level);
// save the models
foreach($level->getModels() as $model) {
// var_dump($model);
$em->persist($model);
}
}
// update database
$em->flush();
// success message
$this->addFlash('notice', 'Catalog tree structure has been created successfully !');
# redirection
// return $this->redirectToRoute('catalog_tree_edit', array('code' => $code));
}
// show page
return $this->render('catalogs/tree.html.twig', array(
'h1_title' => 'Tree structure of the catalog : ' . $catalog->getName() . ' (' . $catalog->getCode() . ')',
'form' => $form->createView(),
'code' => $catalog->getCode()
));
}
Results of var_dump :
// save the levels
foreach($catalog->getLevels() as $level) {
// var_dump($level);
$em->persist($level);
// save the models
foreach($level->getModels() as $model) {
// var_dump($model);
$em->persist($model);
}
}
Level :
object(AppBundle\Entity\CatalogLevel)[2610]
private 'id' => string 'NIV_1' (length=5)
private 'catalogCode' => string 'CATEST' (length=6)
private 'name' => string 'genou' (length=5)
private 'parent' => null
private 'order' => int 0
private 'catalog' =>
object(AppBundle\Entity\Catalog)[2493]
private 'code' => string 'CATEST' (length=6)
private 'name' => string 'Catalogue de test' (length=17)
private 'type' => string 'Normal' (length=6)
private 'promo' => null
private 'tariff' => string 'F0' (length=2)
private 'campaign' => string 'FTEST' (length=5)
private 'beginDate' =>
object(DateTime)[2495]
public 'date' => string '2016-06-16 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
private 'endDate' =>
object(DateTime)[2496]
public 'date' => string '2016-06-24 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
private 'catalogSalesForce' =>
object(Doctrine\ORM\PersistentCollection)[2473]
private 'snapshot' =>
array (size=2)
...
private 'owner' =>
&object(AppBundle\Entity\Catalog)[2493]
private 'association' =>
array (size=15)
...
private 'em' =>
object(Doctrine\ORM\EntityManager)[2086]
...
private 'backRefFieldName' => string 'catalog' (length=7)
private 'typeClass' =>
object(Doctrine\ORM\Mapping\ClassMetadata)[2489]
...
private 'isDirty' => boolean false
protected 'collection' =>
object(Doctrine\Common\Collections\ArrayCollection)[2484]
...
protected 'initialized' => boolean true
private 'forces' => null
private 'levels' =>
object(Doctrine\ORM\PersistentCollection)[2459]
private 'snapshot' =>
array (size=0)
...
private 'owner' =>
&object(AppBundle\Entity\Catalog)[2493]
private 'association' =>
array (size=15)
...
private 'em' =>
object(Doctrine\ORM\EntityManager)[2086]
...
private 'backRefFieldName' => string 'catalog' (length=7)
private 'typeClass' =>
object(Doctrine\ORM\Mapping\ClassMetadata)[2468]
...
private 'isDirty' => boolean true
protected 'collection' =>
object(Doctrine\Common\Collections\ArrayCollection)[2442]
...
protected 'initialized' => boolean true
private 'models' =>
object(Doctrine\Common\Collections\ArrayCollection)[2708]
private 'elements' =>
array (size=1)
0 =>
object(AppBundle\Entity\CatalogModel)[2735]
Model :
object(AppBundle\Entity\CatalogModel)[2735]
private 'code' => string 'BARACCBA' (length=8)
private 'catalogCode' => string 'CATEST' (length=6)
private 'levelId' => string 'NIV_1' (length=5)
private 'order' => int 0
private 'level' =>
object(AppBundle\Entity\CatalogLevel)[2610]
private 'id' => string 'NIV_1' (length=5)
private 'catalogCode' => string 'CATEST' (length=6)
private 'name' => string 'genou' (length=5)
private 'parent' => null
private 'order' => int 0
private 'catalog' =>
object(AppBundle\Entity\Catalog)[2493]
private 'code' => string 'CATEST' (length=6)
private 'name' => string 'Catalogue de test' (length=17)
private 'type' => string 'Normal' (length=6)
private 'promo' => null
private 'tariff' => string 'F0' (length=2)
private 'campaign' => string 'FTEST' (length=5)
private 'beginDate' =>
object(DateTime)[2495]
...
private 'endDate' =>
object(DateTime)[2496]
...
private 'catalogSalesForce' =>
object(Doctrine\ORM\PersistentCollection)[2473]
...
private 'forces' => null
private 'levels' =>
object(Doctrine\ORM\PersistentCollection)[2459]
...
private 'models' =>
object(Doctrine\Common\Collections\ArrayCollection)[2708]
private 'elements' =>
array (size=1)
...
private 'catalog' => null
private 'model' => null
private 'beginDate' =>
object(DateTime)[2722]
public 'date' => string '1905-05-05 00:00:00.000000' (length=26)
public 'timezone_type' => int 1
public 'timezone' => string '+00:00' (length=6)
private 'endDate' =>
object(DateTime)[2772]
public 'date' => string '1905-05-05 00:00:00.000000' (length=26)
public 'timezone_type' => int 1
public 'timezone' => string '+00:00' (length=6)
Request details :
"START TRANSACTION"
Parameters: { }
INSERT INTO catalogue_niveau (Niveau_ID, Catalogue_Code, Niveau_Nom, Parent_ID, Niveau_Ordre) VALUES (?, ?, ?, ?, ?)
Parameters: { 1: NIV_1, 2: CATEST, 3: genou, 4: null, 5: 0 }
INSERT INTO catalogue_rayon (Modele_Code, Catalogue_Code, Niveau_ID, Modele_Ordre, Date_Debut, Date_Fin) VALUES (?, ?, ?, ?, ?, ?)
Parameters: { 1: null, 2: null, 3: NIV_1, 4: 0, 5: '1905-05-05 00:00:00', 6: '1905-05-05 00:00:00' }
I don't understand why model values are lost... Please help !
PS : If I create just levels in my tree, with no models, levels are saved in database. No error. So, I have a problem with models.
Related
Howe create custom repository QueryBuilder for Room entity, witch will return all available rooms between two dates. I have two Entities Room.php and Reservation.php In RoomRepository.php i create function findAvailableRooms($from, $to) Hove i create query builder who return all rooms who do not have a reservation in the selected period? Tnx Room.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Controller\AvailableRoom;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\IdGenerator\UuidV4Generator;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Uid\Uuid;
use App\Repository\RoomRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Controller\AvailableRoomController;
/**
* #ApiResource(
* normalizationContext={"groups"={"room:read"}},
* denormalizationContext={"groups"={"room:write"}},
* attributes={
* "pagination_client_items_per_page"=true
* },
* collectionOperations={
* "get",
* "post",
* "get_available"={
* "method"="GET",
* "path"="/rooms/available/{from}/{to}",
* "controller"=AvailableRoomController::class
* }
* }
* )
* #ORM\Entity(repositoryClass=RoomRepository::class)
*/
class Room
{
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UuidV4Generator::class)
* #Groups({"room:read"})
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"room:read", "room:write", "reservation:read"})
*/
private $name;
/**
* #ORM\Column(type="string", length=255)
* #Gedmo\Slug(fields={"name"})
* #Groups({"room:read", "room:write"})
*/
private $slug;
/**
* #ORM\Column(type="text", nullable=true)
* #Groups({"room:read", "room:write"})
*/
private $description;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="create")
* #Groups({"room:read", "room:write"})
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="update")
* #Groups({"room:read", "room:write"})
*/
private $updatedAt;
/**
* #ORM\OneToMany(targetEntity=Reservation::class, mappedBy="room")
* #Groups({"room:read", "room:write"})
*/
private $reservations;
public function __construct()
{
$this->reservations = new ArrayCollection();
}
public function getId(): ?Uuid
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(?\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* #return Collection|Reservation[]
*/
public function getReservations(): Collection
{
return $this->reservations;
}
public function addReservation(Reservation $reservation): self
{
if (!$this->reservations->contains($reservation)) {
$this->reservations[] = $reservation;
$reservation->setRoom($this);
}
return $this;
}
public function removeReservation(Reservation $reservation): self
{
if ($this->reservations->removeElement($reservation)) {
// set the owning side to null (unless already changed)
if ($reservation->getRoom() === $this) {
$reservation->setRoom(null);
}
}
return $this;
}
}
Reservation.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\DateFilter;
use App\Repository\ReservationRepository;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\IdGenerator\UuidV4Generator;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Uid\Uuid;
/**
* #ApiResource(
* normalizationContext={"groups"={"reservation:read"}},
* denormalizationContext={"groups"={"reservation:write"}},
* attributes={
* "pagination_client_items_per_page"=true
* }
* )
* #ApiFilter(DateFilter::class, properties={"dateFrom"})
* #ORM\Entity(repositoryClass=ReservationRepository::class)
*/
class Reservation
{
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UuidV4Generator::class)
* #Groups({"reservation:read"})
*/
private $id;
/**
* #ORM\Column(type="date")
* #Groups({"reservation:read", "reservation:write", "room:read"})
*/
private $dateFrom;
/**
* #ORM\Column(type="date")
* #Groups({"reservation:read", "reservation:write", "room:read"})
*/
private $dateTo;
/**
* #ORM\Column(type="json", nullable=true)
* #Groups({"reservation:read", "reservation:write", "room:read"})
*/
private $meta = [];
/**
* #ORM\ManyToOne(targetEntity=Room::class, inversedBy="reservations")
* #Groups({"reservation:read", "reservation:write"})
*/
private $room;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="create")
* #Groups({"reservation:read", "reservation:write", "room:read"})
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="update")
* #Groups({"reservation:read", "reservation:write", "room:read"})
*/
private $updatedAt;
public function getId(): ?Uuid
{
return $this->id;
}
public function getDateFrom(): ?\DateTimeInterface
{
return $this->dateFrom;
}
public function setDateFrom(\DateTimeInterface $dateFrom): self
{
$this->dateFrom = $dateFrom;
return $this;
}
public function getDateTo(): ?\DateTimeInterface
{
return $this->dateTo;
}
public function setDateTo(\DateTimeInterface $dateTo): self
{
$this->dateTo = $dateTo;
return $this;
}
public function getMeta(): ?array
{
return $this->meta;
}
public function setMeta(?array $meta): self
{
$this->meta = $meta;
return $this;
}
public function getRoom(): ?Room
{
return $this->room;
}
public function setRoom(?Room $room): self
{
$this->room = $room;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(?\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
}
Something like the following method placed in your ReservationRepository will fetch the ids of Rooms having a Reservation overlapping a specified date range:
/**
* #param \DateTimeInterface $start
* #param \DateTimeInterface $end
*
* #return array
*/
public function findRoomIdsReservedInDateRange(\DateTimeInterface $start, \DateTimeInterface $end)
{
$result = $this
->createQueryBuilder('r')
->select('r.room.id')
->andWhere('r.dateFrom < :end')
->andWhere('r.dateTo > :start')
->setParameter('start', $start)
->setParameter('end', $end)
->orderBy('r.room.id', 'ASC')
->getQuery()
->getScalarResult()
; // [['id' => 1], ['id' => 2], ['id' => 2], ['id' => 5]]
return array_unique(array_column($result, 'id')); // [1, 2, 5]
}
Then you could call that method from your RoomRepository
$exclude = $reservationRepository->findRoomIdsReservedInDateRange($start, $end);
return $this
->createQueryBuilder('room')
->andWhere('room.id not in :array')
->setParameter('array', $exclude)
->getQuery()
->getResult()
;
This solution was working til TYPO3 8.7. But breaks in TYPO3 9.
I've some submenu showing images from page-resources and using categories.descriptions for css.
I use custom viewhelpers for that. But now the JOIN-sql is broken... Don't know why, because it worked in 8.7 and previous.
<?php
namespace EnzephaloN\ThemePsoa\ViewHelper;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
class SysCategoryViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* #var integer $uid (CE)
*/
public function initializeArguments() {
$this->registerArgument('uid', 'integer', 'enthaelt die UID des CE', TRUE);
}
/**
* #var integer $uid
*/
public function render($uid = null) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_category');
$queryBuilder->getRestrictions()
->removeAll();
$query = $queryBuilder
->select('*')
->from('sys_category')
->join('sys_category','sys_category_record_mm','MM', $queryBuilder->expr()->andX($queryBuilder->expr()->eq('MM.uid_foreign', $uid),$queryBuilder->expr()->eq('MM.uid_local', 'uid')))
->setMaxResults(1);
$result = $query->execute();
$res = [];
while ($row = $result->fetch()) {
$res[] = $row;
}
$this->templateVariableContainer->add('sysCategoryDetailArray', $res);
}
}
ERRORMESSAGE
(1/2) Doctrine\DBAL\Exception\SyntaxErrorException
An exception occurred while executing
SELECT * FROM sys_category INNER JOIN sys_category_record_mm MM ON
(`MM`.`uid_foreign` = ) AND (MM.uid_local = uid)
LIMIT 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') AND (MM.uid_local = uid) LIMIT 1' at line 1
and for fal:
<?php
namespace EnzephaloN\ThemePsoa\ViewHelper;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
class FalResourceViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* #author INGENIUMDESIGN
*/
public function initializeArguments() {
$this->registerArgument('uid', 'integer', 'enthaelt die UID des CE', TRUE);
}
/**
* #var mixed $uid
*/
public function render($uid = null) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file');
if($uid) {
$query = $queryBuilder
->select('*')
->from('sys_file')
->join('sys_file', 'sys_file_reference', 'MM',
$queryBuilder->expr()->andX(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('MM.uid_foreign', $uid),
$queryBuilder->expr()->eq('MM.tablenames', '"pages"')
),
$queryBuilder->expr()->eq('MM.uid_local', 'sys_file.uid')
)
)
->setMaxResults(1);
//die($query->getSQL());
$result = $query->execute();
$res = [];
while ($row = $result->fetch()) {
$res[] = $row;
}
$this->templateVariableContainer->add('sysFileArray', $res);
}
}
}
Can anybody check those queryBuilder-statements?
Thanx in advance
EnzephaloN
Here's the solution:
FalResourceViewhelper
Just use the TYPO3-core-method $fileRepository->findByRelation(); This returns the page-related resources.
Use like this:
<e:falResource data="{page.data}" table="pages" field="media" as="sysFileArray">
<f:if condition="{sysFileArray.0.uid}!=0">
<f:image src="{sysFileArray.0.uid}" treatIdAsReference="1" class="img-responsive"/>
</f:if>
</e:falResouce>
<?php
namespace EnzephaloN\ThemePsoa\ViewHelper;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* FalViewHelper
*/
class FalResourceViewHelper extends AbstractViewHelper{
use CompileWithRenderStatic;
/**
* #var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* #throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('data', 'array', 'Data of current record', true);
$this->registerArgument('table', 'string', 'table', false, 'tt_content');
$this->registerArgument('field', 'string', 'field', false, 'image');
$this->registerArgument('as', 'string', 'Name of variable to create', false, 'items');
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
$variableProvider = $renderingContext->getVariableProvider();
if (is_array($arguments['data']) && $arguments['data']['uid'] && $arguments['data'][$arguments['field']]) {
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
$items = $fileRepository->findByRelation(
$arguments['table'],
$arguments['field'],
$arguments['data']['uid']
);
$localizedId = null;
if (isset($arguments['data']['_LOCALIZED_UID'])) {
$localizedId = $arguments['data']['_LOCALIZED_UID'];
} elseif (isset($arguments['data']['_PAGES_OVERLAY_UID'])) {
$localizedId = $arguments['data']['_PAGES_OVERLAY_UID'];
}
$isTableLocalizable = (
!empty($GLOBALS['TCA'][$arguments['table']]['ctrl']['languageField'])
&& !empty($GLOBALS['TCA'][$arguments['table']]['ctrl']['transOrigPointerField'])
);
if ($isTableLocalizable && $localizedId !== null) {
$items = $fileRepository->findByRelation($arguments['table'], $arguments['field'], $localizedId);
}
} else {
$items = null;
}
$variableProvider->add($arguments['as'], $items);
$content = $renderChildrenClosure();
$variableProvider->remove($arguments['as']);
return $content;
}
}
It's easy.
--
SysCategoryViewHelper works like this:
<e:sysCategory data="{page.data}" as="sysCategoryDetailArray">
<f:if condition="{sysCategoryDetailArray.0}">
<i class="fa fa-4x {sysCategoryDetailArray.0.description}"></i>
</f:if>
</e:sysCategory>
And here is the code:
<?php
namespace EnzephaloN\ThemePsoa\ViewHelper;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
use TYPO3\CMS\Core\Database\ConnectionPool;
class SysCategoryViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper {
use CompileWithRenderStatic;
/**
* #var bool
*/
protected $escapeOutput = false;
/**
* Initialize arguments.
*
* #throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('data', 'array', 'Data of current record', true);
$this->registerArgument('as', 'string', 'Name of variable to create', false, 'items');
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) {
$variableProvider = $renderingContext->getVariableProvider();
if (is_array($arguments['data']) && $arguments['data']['uid']) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_category');
$queryBuilder->getRestrictions()
->removeAll();
$query = $queryBuilder
->select('*')
->from('sys_category')
->join('sys_category', 'sys_category_record_mm', 'MM', $queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('MM.uid_foreign', $arguments['data']['uid']),
$queryBuilder->expr()->eq('MM.uid_local','sys_category.uid')));
$result = $query->execute();
$items = [];
while ($row = $result->fetch()) {
$items[] = $row;
}
} else {
$items = null;
}
$variableProvider->add($arguments['as'], $items);
$content = $renderChildrenClosure();
$variableProvider->remove($arguments['as']);
return $content;
}
}
I have an existing application with line below
\Yii::$container->invoke([$this, 'injections']);
and this line couses the error
ReflectionException Class Redis does not exist
I have a file Redis.php where defined class Redis in the /common/components directory.
But yii looks for it at the /common/components/myAPI/
А class that contains a line
\Yii::$container->invoke([$this, 'injections']);
ia located by the path /common/components/myAPI/
The whole class where I try to call the Redis
abstract class AEntity{
const API_BE_SOCCER_COUNTER = 'API_BE_SOCCER_COUNTER';
/**
* #var Fetcher $_fetcher
* #var boolean $_isFromCache
* #var APIConfig $_config
* #var string $_redisKey
* #var array $_params
*/
private $_fetcher,
$_isFromCache = null,
$_params = [],
$_redisKey = null,
$_mainApi;
/**
* #var APIRedisManager $_redis
*/
private $_redis,
$_config;
/**
* #var Array $_dbTable
* #var Array $_dbMap
*/
protected $_dbMap,
$_dbTable;
/**
*
* #return array
*/
protected function setParams(Array $params = []){
$this->_params = $params;
}
protected abstract function keyRelationShip();
protected abstract function getAttributes();
protected abstract function jsonArrayMap();
protected function setRedisKey($redisData){
$this->_redisKey = $redisData;
}
protected function mapArrays(){
$jsonArrayMap = $this->jsonArrayMap();
foreach ($jsonArrayMap as $arrayMap){
$mapPath = $arrayMap['path'] ? explode('.', $arrayMap['path']) : null;
$key = $arrayMap['key'];
$arr = $arrayMap['array'];
$assoc = $arrayMap['assoc'];
$tmpData = $this->_mainApi;
if($mapPath){
foreach($mapPath as $mapper) {
if(!isset($tmpData[$mapper])){
$this->{$key} = null;
return false;
}
$tmpData = $tmpData[$mapper];
}
}
$this->{$key} = $arr ? $this->jsonArrayMapper($tmpData, $assoc, $mapPath) : $tmpData;
}
}
public function injections(APIConfig $config, Fetcher $fetcher, APIRedisManager $redisManager){
$this->_config = $config;
$this->_fetcher = $fetcher;
$this->_redis = $redisManager;
}
protected function initialize(Array $params = []){
$constant = static::keyRelationShip();
$redisKey = $this->paramToRedis($params);
$this->setParams($params);
$this->setRedisKey($redisKey);
\Yii::$container->invoke([$this, 'injections']);
$this->_config->get($constant, $this->_params);
$this->_redis->setConfig($this->_config);
$this->_fetcher->setConfig($this->_config);
$this->_mainApi = $this->getAPIRequest();
$this->mapArrays();
}
/**
* #return array
*/
public function getMainApi(){
return $this->_mainApi;
}
/**
* #return APIRedisManager
*/
public function getRedis(){
return $this->_redis;
}
/**
* #return array
* #throws \Exception
*/
public function loadYiiData(){
$arrModel = [];
if (!$this->_dbTable) new Exception('No Table Specified.');
if (!$this->_dbMap) new Exception('No DB Map Specified.');
foreach ($this->_dbMap as $keyApi => $keyDB){
if(!isset($this->$keyDB)) throw new \Exception("KeyDB: $keyDB, is not Set");
$arrModel[$this->_dbTable][$keyApi] = $this->$keyDB;
}
return $arrModel;
}
/**
* GET API request logic
*
* #return array
*/
public function getAPIRequest(){
$redisKey = $this->formulateRedisKeyLogic();
$storedRequest = $this->_redis->getConfig() ? $this->_redis->get($redisKey) : null;
if(!$storedRequest){
$this->_isFromCache = false;
$apiRequestResult = $this->_fetcher->get()->asArray();
$this->_redis->incrCounter();
if($apiRequestResult && !$storedRequest){
$serializedApiRequest = serialize($apiRequestResult);
$this->_redis->store($serializedApiRequest, $redisKey);
}
}else{
$this->_isFromCache = true;
$apiRequestResult = unserialize($storedRequest);
}
return $apiRequestResult;
}
/** #return boolean */
public function isFromCache(){
return $this->_isFromCache;
}
private function formulateRedisKeyLogic(){
$config = $this->_redis->getConfig();
if(isset($config['key']) && strpos($this->_redisKey,'$.')!==false){
$configKey = $config['key'];
$redisKey = $configKey . $this->_redisKey;
$redisKey = str_replace('$.', '', $redisKey);
}
else{
$redisKey = $this->_redisKey;
}
return $redisKey;
}
protected function paramToRedis($param){
$className = (new \ReflectionClass($this))->getShortName();
$buildRedisKey = '$._'.str_replace('=', '_', http_build_query($param, null, ','));
$paramKey = $buildRedisKey.'_'.$className;
return $paramKey;
}
/**
* GET API request logic
*
* #return array
*/
protected function jsonArrayMapper(Array $entityItems, $assoc = false, $mapPath= true){
$aEntityArray = [];
$attributes = $this->getAttributes();
$Klass = static::class;
if($mapPath){
foreach ($entityItems as $entityItem){
$aEntity = new $Klass(false);
foreach ($attributes as $attribute){
$aEntity->{$attribute} = $entityItem[$attribute];
}
$assoc ? $aEntityArray[$entityItem[$assoc]] = $aEntity : $aEntityArray[] = $aEntity;
}
}else{
$aEntity = new $Klass(false);
foreach ($attributes as $attribute){
$aEntity->{$attribute} = $entityItems[$attribute];
}
$aEntityArray = $aEntity;
}
return $aEntityArray;
}
public function __set($key, $value){
$this->{$key} = $value;
}
public function __get($name) {
return $this->{$key};
}
}
This is a super class for class those have such constructor
public function __construct($fullInitizalization=true, $params = []) {
if($fullInitizalization){
$redisParams = $this->paramToRedis($params);
parent::initialize($params);
}
}
When DI container trying to instaniate APIRedisConnection class, it passes parameter with type Redis:
/** #param Redis $redis */
function __construct(Redis $redis){
$this->_redis = $redis;
}
Class Redis can't be found in the project, but I can see it in IDE and this class was written on PHP 7
Although the whole project written on PHP 5.6
I have been programming a project in the Symfony 3 using the Doctrine ORM. I have main entity called 'Race', which contains some fields and relations to another entities like 'RaceRelationToType', 'RaceRelationToOrganizer' and so on - you can see it in the picture of the database schema bellow. (Only for information, 'RaceRelationTo*' entities are entities which prevents change of original data.)
The problem: The problem becames when I fetch data from database using $this->em->getRepository('AppBundle:Race')->findAll();. Data in database are stored correctly, but If I fetch them, they have wrong data type. raceRelationToOrganizer variable should be of data type RaceRelationToOrganizer, not RaceRelationToSeason and so on.
Everything had worked right until I reset a database. When I insert new race to cleared database, id of raceRelationToSeason is 1, raceRelationToType is 1, raceRelationToOrganizer is 1 ans so on. Problems occured after reset of database (and reset of auto increment).
If ids of all relations are different, than it works OK. Magic? Error? Does anybody know what to do with it? Can you find some problem in files bellow? I have been trying to solve it for hours. Contents of important files are bellow.
I use yaml configuration files for ORM mapping. Race.orm.yml contains:
AppBundle\Entity\Race:
type: entity
table: race
id:
id:
type: integer
generator:
strategy: AUTO
fields:
registrationNumberOfRace:
column: registration_number_of_race
type: integer
length: 4
nullable: false
dateFrom:
column: date_from
type: date
nullable: false
dateTo:
column: date_to
type: date
nullable: false
manyToOne:
raceRelationToSeason:
targetEntity: AppBundle\Entity\RaceRelationToSeason
joinColumn:
name: race_relation_to_season
referencedColumnName: id
onDelete: RESTRICT
nullable: false
fetch: EAGER
cascade: [ persist,remove ]
raceRelationToType:
targetEntity: AppBundle\Entity\RaceRelationToType
joinColumn:
name: race_relation_to_type
referencedColumnName: id
onDelete: RESTRICT
nullable: false
fetch: EAGER
cascade: [ persist,remove ]
raceRelationToOrganizer:
targetEntity: AppBundle\Entity\RaceRelationToOrganizer
joinColumn:
name: race_relation_to_organizer
referencedColumnName: id
onDelete: RESTRICT
nullable: false
fetch: EAGER
cascade: [ persist,remove ]
raceRelationToVenue:
targetEntity: AppBundle\Entity\RaceRelationToVenue
joinColumn:
name: race_relation_to_venue
referencedColumnName: id
onDelete: RESTRICT
nullable: false
fetch: EAGER
cascade: [ persist,remove ]
Entity Race.php contains:
<?php
namespace AppBundle\Entity;
/**
* Class Race
* #package AppBundle\Entity
*/
class Race
{
/**
* #var integer Id of the race
*/
protected $id;
/**
* #var SeasonOfRace
*/
protected $seasonOfRace;
/**
* #var RaceRelationToSeason
*/
protected $raceRelationToSeason;
/**
* #var TypeOfRace
*/
protected $typeOfRace;
/**
* #var RaceRelationToType
*/
protected $raceRelationToType;
/**
* #var integer Registration number of the race which depend on the type of race
*/
protected $registrationNumberOfRace;
/**
* #var OrganizerOfRace
*/
protected $organizerOfRace;
/**
* #var RaceRelationToOrganizer
*/
protected $raceRelationToOrganizer;
/**
* #var VenueOfRace
*/
protected $venueOfRace;
/**
* #var RaceRelationToVenue
*/
protected $raceRelationToVenue;
/**
* #var \DateTime
*/
protected $dateFrom;
/**
* #var \DateTime
*/
protected $dateTo;
/**
* Race constructor.
*/
public function __construct()
{
$this->dateFrom = $this->dateTo = new \DateTime();
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param $id
* #return $this
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* #return SeasonOfRace
*/
public function getSeasonOfRace()
{
if ($this->raceRelationToSeason) {
if ($this->raceRelationToSeason->getOriginal()) {
$this->seasonOfRace = $this->raceRelationToSeason->getOriginal();
return $this->seasonOfRace;
} else {
return $this->seasonOfRace;
}
} else {
return $this->seasonOfRace;
}
}
/**
* #param SeasonOfRace $seasonOfRace
* #return Race
*/
public function setSeasonOfRace(SeasonOfRace $seasonOfRace)
{
$this->seasonOfRace = $seasonOfRace;
if ($this->seasonOfRace) {
if ($this->raceRelationToSeason) {
$this->raceRelationToSeason->setLabel($this->seasonOfRace->getLabel());
$this->raceRelationToSeason->setShortLabel($this->seasonOfRace->getShortLabel());
$this->raceRelationToSeason->setOriginal($this->seasonOfRace);
} else {
$this->raceRelationToSeason = new RaceRelationToSeason();
$this->raceRelationToSeason->setLabel($this->seasonOfRace->getLabel());
$this->raceRelationToSeason->setShortLabel($this->seasonOfRace->getShortLabel());
$this->raceRelationToSeason->setOriginal($this->seasonOfRace);
}
}
return $this;
}
/**
* #return RaceRelationToSeason
*/
public function getRaceRelationToSeason()
{
return $this->raceRelationToSeason;
}
/**
* #param RaceRelationToSeason $raceRelationToSeason
* #return Race
*/
public function setRaceRelationToSeason(RaceRelationToSeason $raceRelationToSeason)
{
$this->raceRelationToSeason = $raceRelationToSeason;
return $this;
}
/**
* #return TypeOfRace
*/
public function getTypeOfRace()
{
if ($this->raceRelationToType) {
if ($this->raceRelationToType->getOriginal()) {
$this->typeOfRace = $this->raceRelationToType->getOriginal();
return $this->typeOfRace;
} else {
return $this->typeOfRace;
}
} else {
return $this->typeOfRace;
}
}
/**
* #param TypeOfRace $typeOfRace
* #return Race
*/
public function setTypeOfRace(TypeOfRace $typeOfRace)
{
$this->typeOfRace = $typeOfRace;
if ($this->typeOfRace) {
if ($this->raceRelationToType) {
$this->raceRelationToType->setLabel($this->typeOfRace->getLabel());
$this->raceRelationToType->setShortLabel($this->typeOfRace->getShortLabel());
$this->raceRelationToType->setOriginal($this->typeOfRace);
} else {
$this->raceRelationToType = new RaceRelationToType();
$this->raceRelationToType->setLabel($this->typeOfRace->getLabel());
$this->raceRelationToType->setShortLabel($this->typeOfRace->getShortLabel());
$this->raceRelationToType->setOriginal($this->typeOfRace);
}
}
return $this;
}
/**
* #return RaceRelationToType
*/
public function getRaceRelationToType()
{
return $this->raceRelationToType;
}
/**
* #param RaceRelationToType $raceRelationToType
* #return Race
*/
public function setRaceRelationToType(RaceRelationToType $raceRelationToType)
{
$this->raceRelationToType = $raceRelationToType;
return $this;
}
/**
* #return int
*/
public function getRegistrationNumberOfRace()
{
return $this->registrationNumberOfRace;
}
/**
* #param $registrationNumberOfRace
* #return Race
*/
public function setRegistrationNumberOfRace($registrationNumberOfRace)
{
$this->registrationNumberOfRace = $registrationNumberOfRace;
return $this;
}
/**
* #return VenueOfRace
*/
public function getVenueOfRace()
{
if ($this->raceRelationToVenue) {
if ($this->raceRelationToVenue->getOriginal()) {
$this->venueOfRace = $this->raceRelationToVenue->getOriginal();
return $this->venueOfRace;
} else {
return $this->venueOfRace;
}
} else {
return $this->venueOfRace;
}
}
/**
* #param VenueOfRace $venueOfRace
* #return Race
*/
public function setVenueOfRace(VenueOfRace $venueOfRace)
{
$this->venueOfRace = $venueOfRace;
if ($this->venueOfRace) {
if ($this->raceRelationToVenue) {
$this->raceRelationToVenue->setLabel($this->venueOfRace->getLabel());
$this->raceRelationToVenue->setShortLabel($this->venueOfRace->getShortLabel());
$this->raceRelationToVenue->setOriginal($this->venueOfRace);
} else {
$this->raceRelationToVenue = new RaceRelationToVenue();
$this->raceRelationToVenue->setLabel($this->venueOfRace->getLabel());
$this->raceRelationToVenue->setShortLabel($this->venueOfRace->getShortLabel());
$this->raceRelationToVenue->setOriginal($this->venueOfRace);
}
}
return $this;
}
/**
* #return RaceRelationToVenue
*/
public function getRaceRelationToVenue()
{
return $this->raceRelationToVenue;
}
/**
* #param RaceRelationToVenue $raceRelationToVenue
* #return Race
*/
public function setRaceRelationToVenue(RaceRelationToVenue $raceRelationToVenue)
{
$this->raceRelationToVenue = $raceRelationToVenue;
return $this;
}
/**
* #return OrganizerOfRace
*/
public function getOrganizerOfRace()
{
if ($this->raceRelationToOrganizer) {
if ($this->raceRelationToOrganizer->getOriginal()) {
$this->organizerOfRace = $this->raceRelationToOrganizer->getOriginal();
return $this->organizerOfRace;
} else {
return $this->organizerOfRace;
}
} else {
return $this->organizerOfRace;
}
}
/**
* #param OrganizerOfRace $organizerOfRace
* #return Race
*/
public function setOrganizerOfRace(OrganizerOfRace $organizerOfRace)
{
$this->organizerOfRace = $organizerOfRace;
if ($this->organizerOfRace) {
if ($this->raceRelationToOrganizer) {
$this->raceRelationToOrganizer->setLabel($this->organizerOfRace->getLabel());
$this->raceRelationToOrganizer->setShortLabel($this->organizerOfRace->getShortLabel());
$this->raceRelationToOrganizer->setOriginal($this->organizerOfRace);
} else {
$this->raceRelationToOrganizer = new RaceRelationToOrganizer();
$this->raceRelationToOrganizer->setLabel($this->organizerOfRace->getLabel());
$this->raceRelationToOrganizer->setShortLabel($this->organizerOfRace->getShortLabel());
$this->raceRelationToOrganizer->setOriginal($this->organizerOfRace);
}
}
return $this;
}
/**
* #return RaceRelationToOrganizer
*/
public function getRaceRelationToOrganizer()
{
return $this->raceRelationToOrganizer;
}
/**
* #param RaceRelationToOrganizer $raceRelationToOrganizer
* #return Race
*/
public function setRaceRelationToOrganizer(RaceRelationToOrganizer $raceRelationToOrganizer)
{
$this->raceRelationToOrganizer = $raceRelationToOrganizer;
return $this;
}
/**
* #return \DateTime
*/
public function getDateFrom()
{
return $this->dateFrom;
}
/**
* #param \DateTime $dateFrom
* #return $this
*/
public function setDateFrom(\DateTime $dateFrom)
{
$this->dateFrom = $dateFrom;
return $this;
}
/**
* #return \DateTime
*/
public function getDateTo()
{
return $this->dateTo;
}
/**
* #param \DateTime $dateTo
*/
public function setDateTo(\DateTime $dateTo)
{
$this->dateTo = $dateTo;
}
}
I solved it exactly after 23 hours, it was my fault. I want to say sorry to all who have spent their time with trying to help me.
As you can see in the database schema above, there is the table RaceRelationToType that is table between table called Race and table called TypeOfRace. This table is here because when you remove original data from TypeOfRace, data are still stored in a copy in RaceRelationToType+ - it is because of archiving data. It is same with another tables beggining with RaceRelationToXXX.
Mistake was in RaceRelationToOrganizer.php entity. I had extended RaceRelationToOrganizer entity with VenueOfRace entity instead of OrganizerOfRace entity. I don't know why, but It had worked before, but not after reset of database.
So there was no error shown, but Doctrine didn't know what to do with it (fields in all this between-tables are identical) and it mapped random data types. So that's all.
Thank you and sorry!
I want to create an SQL view within the codeigniter model. What is the best way of doing this?
I use mutliple models depending on wich table i need to work
application/core/MY_model.php
<?php
/**
* CRUD Model
* A base model providing CRUD, pagination and validation.
*/
class MY_Model extends CI_Model
{
public $table;
public $primary_key;
public $default_limit = 15;
public $page_links;
public $query;
public $form_values = array();
protected $default_validation_rules = 'validation_rules';
protected $validation_rules;
public $validation_errors;
public $total_rows;
public $date_created_field;
public $date_modified_field;
public $native_methods = array(
'select', 'select_max', 'select_min', 'select_avg', 'select_sum', 'join',
'where', 'or_where', 'where_in', 'or_where_in', 'where_not_in', 'or_where_not_in',
'like', 'or_like', 'not_like', 'or_not_like', 'group_by', 'distinct', 'having',
'or_having', 'order_by', 'limit'
);
function __construct()
{
parent::__construct();
}
public function __call($name, $arguments)
{
call_user_func_array(array($this->db, $name), $arguments);
return $this;
}
/**
* Sets CI query object and automatically creates active record query
* based on methods in child model.
* $this->model_name->get()
*/
public function get($with = array(), $include_defaults = true)
{
if ($include_defaults) {
$this->set_defaults();
}
foreach ($with as $method) {
$this->$method();
}
$this->query = $this->db->get($this->table);
return $this;
}
/**
* Query builder which listens to methods in child model.
* #param type $exclude
*/
private function set_defaults($exclude = array())
{
$native_methods = $this->native_methods;
foreach ($exclude as $unset_method) {
unset($native_methods[array_search($unset_method, $native_methods)]);
}
foreach ($native_methods as $native_method) {
$native_method = 'default_' . $native_method;
if (method_exists($this, $native_method)) {
$this->$native_method();
}
}
}
/**
* Call when paginating results.
* $this->model_name->paginate()
*/
public function paginate($with = array())
{
$uri_segment = '';
$offset = 0;
$per_page = $this->default_limit;
$this->load->helper('url');
$this->load->library('pagination');
$this->set_defaults();
foreach ($with as $method) {
$this->$method();
}
$this->total_rows = $this->db->count_all_results($this->table);
$uri_segments = $this->uri->segment_array();
foreach ($uri_segments as $key => $segment) {
if ($segment == 'page') {
$uri_segment = $key + 1;
if (isset($uri_segments[$uri_segment])) {
$offset = $uri_segments[$uri_segment];
}
unset($uri_segments[$key], $uri_segments[$key + 1]);
$base_url = site_url(implode('/', $uri_segments) . '/page/');
}
}
if (!$uri_segment) {
$base_url = site_url($this->uri->uri_string() . '/page/');
}
$config = array(
'base_url' => $base_url,
'uri_segment' => $uri_segment,
'total_rows' => $this->total_rows,
'per_page' => $per_page
);
if ($this->config->item('pagination_style')) {
$config = array_merge($config, $this->config->item('pagination_style'));
}
$this->pagination->initialize($config);
$this->page_links = $this->pagination->create_links();
/**
* Done with pagination, now on to the paged results
*/
$this->set_defaults();
foreach ($with as $method) {
$this->$method();
}
$this->db->limit($per_page, $offset);
$this->query = $this->db->get($this->table);
return $this;
}
function paginate_api($limit, $offset)
{
$this->set_defaults();
if (empty($limit)) {
$limit = $this->default_limit;
}
if (empty($offset)) {
$offset = 0;
}
$this->db->limit($limit, $offset);
return $this->db->get($this->table)->result();
}
/**
* Retrieves a single record based on primary key value.
*/
public function get_by_id($id, $with = array())
{
foreach ($with as $method) {
$this->$method();
}
return $this->where($this->primary_key, $id)->get()->row();
}
public function save($id = NULL, $db_array = NULL)
{
if (!$db_array) {
$db_array = $this->db_array();
}
if (!$id) {
if ($this->date_created_field) {
$db_array[$this->date_created_field] = time();
}
$this->db->insert($this->table, $db_array);
// $this->session->set_flashdata('alert_success', 'Record successfully created.');
return $this->db->insert_id();
} else {
if ($this->date_modified_field) {
$db_array[$this->date_modified_field] = time();
}
$this->db->where($this->primary_key, $id);
$this->db->update($this->table, $db_array);
// $this->session->set_flashdata('alert_success', 'Record successfully updated.');
return $id;
}
}
/**
* Returns an array based on $_POST input matching the ruleset used to
* validate the form submission.
*/
public function db_array()
{
$db_array = array();
$validation_rules = $this->{$this->validation_rules}();
foreach ($this->input->post() as $key => $value) {
if (array_key_exists($key, $validation_rules)) {
$db_array[$key] = $value;
}
}
return $db_array;
}
/**
* Deletes a record based on primary key value.
* $this->model_name->delete(5);
*/
public function delete($id, $others = array())
{
if (!empty($others)) {
foreach ($others as $k => $v) {
$this->db->where($k, $v);
}
} else {
$this->db->where($this->primary_key, $id);
}
return $this->db->delete($this->table);
// $this->session->set_flashdata('alert_success', 'Record successfully deleted.');
}
/**
* Returns the CI query result object.
* $this->model_name->get()->result();
*/
public function result()
{
return $this->query->result();
}
/**
* Returns the CI query row object.
* $this->model_name->get()->row();
*/
public function row()
{
return $this->query->row();
}
/**
* Returns CI query result array.
* $this->model_name->get()->result_array();
*/
public function result_array()
{
return $this->query->result_array();
}
/**
* Returns CI query row array.
* $this->model_name->get()->row_array();
*/
public function row_array()
{
return $this->query->row_array();
}
/**
* Returns CI query num_rows().
* $this->model_name->get()->num_rows();
*/
public function num_rows()
{
return $this->query->num_rows();
}
/**
* Used to retrieve record by ID and populate $this->form_values.
* #param int $id
*/
public function prep_form($id = NULL)
{
if (!$_POST and ($id)) {
$this->db->where($this->primary_key, $id);
$row = $this->db->get($this->table)->row();
foreach ($row as $key => $value) {
$this->form_values[$key] = $value;
}
}
}
/**
* Performs validation on submitted form. By default, looks for method in
* child model called validation_rules, but can be forced to run validation
* on any method in child model which returns array of validation rules.
* #param string $validation_rules
* #return boolean
*/
public function run_validation($validation_rules = NULL)
{
if (!$validation_rules) {
$validation_rules = $this->default_validation_rules;
}
foreach (array_keys($_POST) as $key) {
$this->form_values[$key] = $this->input->post($key);
}
if (method_exists($this, $validation_rules)) {
$this->validation_rules = $validation_rules;
$this->load->library('form_validation');
$this->form_validation->set_rules($this->$validation_rules());
$run = $this->form_validation->run();
$this->validation_errors = validation_errors();
return $run;
}
}
/**
* Returns the assigned form value to a form input element.
* #param type $key
* #return type
*/
public function form_value($key)
{
return (isset($this->form_values[$key])) ? $this->form_values[$key] : '';
}
}
then when i need a model for a specific database i use class inside models
models/content.php
class content_types extends MY_Model
{
function __construct()
{
parent::__construct();
$this->curentuserdata = $this->session->userdata('lg_result');
$this->userprofile = $this->session->userdata('user_profile');
}
public $table = 'content_types';
public $primary_key = 'id';
public $default_limit = 50;
public function default_order_by()
{
$this->db->order_by('id asc');
}
}
then where i need to call the model
$this->modelname->save($id,array());
$this->modelname->where()->get()->row();
so create one for the 'view table' and the others for insert tables