I have 3 tables:
First table is main table TheSims4Pages. It has Primary Key named id.
Second table is relation table
CREATE TABLE IF NOT EXISTS `TheSims4PagesCategoriesRelations` (
`id` int(11) NOT NULL,
`postId` int(11) NOT NULL,
`catId` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `TheSims4PagesCategoriesRelations`
ADD CONSTRAINT `TheSims4PagesCategoriesRelations_ibfk_2` FOREIGN KEY (`catId`) REFERENCES `TheSims4PagesCategories` (`id`),
ADD CONSTRAINT `TheSims4PagesCategoriesRelations_ibfk_1` FOREIGN KEY (`postId`) REFERENCES `TheSims4Pages` (`id`);
And third table is TheSims4PagesCategories, with PK id.
Currently I have model TheSims4Pages:
<?php
namespace app\models\TheSims4;
use Yii;
/**
* This is the model class for table "TheSims4Pages".
*
* #property integer $id
* #property string $pageName
* #property string $pageEditDate
* #property integer $isDraft
* #property integer $authorId
* #property string $pageText
*
* #property SiteUsers $author
* #property TheSims4PagesCategoriesRelations[] $theSims4PagesCategoriesRelations
*/
class TheSims4Pages extends \yii\db\ActiveRecord {
/**
* #inheritdoc
*/
public static function tableName() {
return 'TheSims4Pages';
}
/**
* #inheritdoc
*/
public function rules() {
return [
[['pageName', 'authorId', 'pageText'], 'required'],
[['pageEditDate'], 'safe'],
[['isDraft', 'authorId'], 'integer'],
[['pageText'], 'string'],
[['pageName'], 'string', 'max' => 500],
[['authorId'], 'exist', 'skipOnError' => true, 'targetClass' => SiteUsers::className(), 'targetAttribute' => ['authorId' => 'id']],
];
}
/**
* #inheritdoc
*/
public function attributeLabels() {
return [
'id' => 'ID',
'pageName' => 'Page Name',
'pageEditDate' => 'Page Edit Date',
'isDraft' => 'Is Draft',
'authorId' => 'Author ID',
'pageText' => 'Page Text',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getAuthor() {
return $this->hasOne(SiteUsers::className(), ['id' => 'authorId']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getTheSims4PagesCategoriesRelations() {
return $this->hasMany(TheSims4PagesCategoriesRelations::className(), ['postId' => 'id']);
}
}
How to make view for TheSims4Pages, with select element for getTheSims4PagesCategoriesRelations of the model, something like
echo $form->field($model, 'categoryId')->dropDownList($listData);
But for relation table?
Ie, I want to make select item with all categories from TheSims4PagesCategories table and save multiple selection of categories into the model
$model = new TheSims4Pages();
Thank you very much!
In view: (where 'categoryName' is column from category table that is used as label)
<?= $form->field($model, 'categories')
->dropDownList(ArrayHelper::map(TheSims4PagesCategories::find()->all(),
'id',
'categoryName'
), ['multiple'=>'multiple']) ?>
In model add virtual variable
// ...
class TheSims4Pages extends \yii\db\ActiveRecord {
public $categories;
public function rules()
{
return [
['categories', 'each', 'rule' => ['integer']],
// your other rules
];
}
// ...
public function afterFind()
{
parent::afterFind();
$this->categories = [];
if (!empty($this->theSims4PagesCategoriesRelations)) {
foreach ($this->theSims4PagesCategoriesRelations as $cat) {
$categories[$cat->catId] = $cat->catId;
}
$this->categories = $categories;
}
}
/* deleting might work better in afterSave
due to ending up with posts without categories on constraint fail
public function beforeSave($insert)
{
parent::beforeSave($insert);
if ($insert) {
} else {
TheSims4PagesCategoriesRelations::deleteAll(['postId' => $this->id]);
}
}
*/
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes);
if ($insert) {
} else {
TheSims4PagesCategoriesRelations::deleteAll(['postId' => $this->id]);
}
if ($this->categories) {
foreach ($this->categories as $cat_id) {
$relation = new TheSims4PagesCategoriesRelations();
$relation->postId = $this->id;
$relation->catId = $cat_id;
$relation->save();
}
}
}
}
Related
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 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.
I've got this table
beds
id
name
size
room
status
hotel
created_at
updated_at
I need to filter all beds that belong to a certain room. In order to do so, I've coded this lines.
public function index()
{
//
$user = JWTAuth::parseToken()->authenticate();
$data = Input::get('room');
if( $data ){
$beds = Bed::where('room', '=', $data )->get();
}else{
$beds = Bed::where('hotel', '=', $user->hostel )->get();
}
foreach( $beds as $bed) {
return $bed->get( array('size','room', 'id') );
}
}
So, If i give it the room id, it should return me only that room's ones.
The thing is that it's returning all table entries.
Any ideas?
UPDATE
Fixed relations and tried this:
return Room::with('beds')->findOrFail($data)->beds;
Now it gives me the number of items.
How can I get the items?
UPDATE
This is the model's code:
class Room extends \Eloquent {
protected $fillable = array('beds', 'price', 'name', 'description','hotel');
public function beds(){
return $this->hasMany('Bed', 'id', 'room');
}
}
UPDATE
The var_dump for:
var_dump( Room::with('beds')->findOrFail($data)->beds );
is:
int(1)
UPDATE
So, the final code is the following.
controller
public function index()
{
//
$user = JWTAuth::parseToken()->authenticate();
$data = Input::get('room');
if( $data ){
$d = intval( $data );
return Bed::where('room', '=', $d )->get( array('size', 'room', 'id', 'name') );
}else{
return Bed::where('hotel', '=', $user->hostel )->get( array('size', 'room', 'id', 'name') );
}
}
model
class Room extends \Eloquent {
protected $fillable = array('beds', 'price', 'name', 'description','hotel');
public function camas(){
return $this->hasMany('Bed', 'room', 'id');
}
}
Thank you guys!
You have quite a few issues in your attempts:
return $bed->get( array('size', 'room', 'id') );
// runs SELECT size, room, id from `rooms`
so it returns all the rooms (why on earth would you like to do this in a foreach anyway?)
return $this->hasMany('Bed', 'id', 'room');
// should be:
return $this->hasMany('Bed', 'room', 'id');
protected $fillable = array('beds', ...
public function beds(){
this is conflict - you will never get a relations when calling $room->beds since you have a column beds on your table.
that said, this is what you need:
public function index()
{
$user = JWTAuth::parseToken()->authenticate();
if(Input::has('room')){
$query = Bed::where('room', '=', Input::get('room'));
}else{
$query = Bed::where('hotel', '=', $user->hostel);
}
return $query->get(['size', 'room', 'id']); // given you need only these columns
}
Try this and see if it works. If not, can you provide the var_dump of Input::get('room') and the structure of the the beds table?
public function index()
{
//
$user = JWTAuth::parseToken()->authenticate();
$data = Input::get('room');
if( $data ){
$beds = Bed::where('room', '=', $data );
}else{
$beds = Bed::where('hotel', '=', $user->hostel );
}
return $beds->get(['size','room', 'id'])->toArray();
}
Better yet if you want to get specific beds in a room and you have your relations set up correctly:
return Room::with('beds')->findOrFail($data)->beds;
EDIT
I saw your update. Are you sure its giving you a number of items, maybe there is one item and the number is the id of it. Can you verify? Please provide a vardump of it if thats not the case. Also can you post your code for the relations in the model?
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
This is my Controller
// No direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport('joomla.application.component.controller');
/**
* Hello World Component Controller
*
* #package Joomla.Tutorials
* #subpackage Components
*/
class HelloController extends JController
{
/**
* Method to display the view
*
* #access public
*/
function __construct($default = array())
{
parent::__construct($default);
// Register Extra tasks
$this->registerTask( 'detail' , 'display' );
}
function display()
{
switch($this->getTask())
{
case 'detail' :
{
JRequest::setVar( 'view' , 'new');
// Checkout the weblink
$model = $this->getModel('hello');
} break;
}
parent::display();
}
}
this is my view.html.php
class HelloViewNew extends JView
{
function display($tpl = null)
{
global $mainframe;
$db =& JFactory::getDBO();
$model =& $this->getModel('hello');
$items = & $model->getdetail();
$this->assignRef( 'items', $items );
parent::display($tpl);
}
}
and this is my model
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'joomla.application.component.model' );
/**
* Hello Model
*
* #package Joomla.Tutorials
* #subpackage Components
*/
class HelloModelHello extends JModel
{
/**
* Gets the greeting
* #return string The greeting to be displayed to the user
*/
var $_data;
/**
* Returns the query
* #return string The query to be used to retrieve the rows from the database
*/
function _buildQuery()
{
$query = ' SELECT * '
. ' FROM #__hello WHERE published = 1'
;
return $query;
}
/**
* Retrieves the hello data
* #return array Array of objects containing the data from the database
*/
function getData()
{
// Lets load the data if it doesn't already exist
if (empty( $this->_data ))
{
$query = $this->_buildQuery();
$this->_data = $this->_getList( $query );
}
//echo "<pre>"; print_r($this->_data); exit;
return $this->_data;
}
function detail()
{
echo "this is test"; exit;
}
}
My question is how can i fetch that detail function from database its not working for me?
on your model, you've the function : function detail() ,
But you've tried to call the function on view with : $items = & $model->getdetail();
Remember your function is detail() NOT getdetail() . So, call with :
$items = & $model->detail();
That's your only mistake I guess so, good luck
you should use this in contoller
$view = $this->getView();
$view->setModel($this->getModel());
then you can use $this->getModel() in view.