I am trying to get this calculate field, UNIX_TIMESTAMP, or as CakePHP calls them, "virtualField".
I have tried this but it didn't work. At my model:
public $virtualFields = array('unixDate' => 'UNIX_TIMESTAMP(Comment.created)');
public function index($idPost){
$comments = $this->Comment->find('all', array(
'conditions'=> array('Comment.idPost' => $idPost),
'order' => 'Comment.id DESC'),
'fields' => array('unixDate'));
}
What am I doing wrong?
Thanks.
Related
I have the following tables/models: A, B, C, BC, D, BCD
(A:B 1:N) Connecting table D to BCD would be no problem. However I would like to filter key attributes as dropdown from A, B, C and D to find results in BCD (because in the end I need BCDid). In BCD next to BCid and Did I can store of course Aid, Bid and Cid, and it would seem to me quite an easy workaround, however I know it's totally against db normalisation. Is there another, better way (with eager loading of course)?
I've now this in BCD:
public function getB() {
return $this->hasOne(\app\models\B::className(), ['id' => 'Bid'])
->via('BC');
}
and it seems to work, but it's not eager loading.
And how do I get to model A? can I define it like this in BCD?:
public function getA() {
return $this->hasOne(\app\models\A::className(), ['id' => 'Aid'])
->via('BC')
->via(B);
}
It doesn't really work yet.
This way it works (BCSearch):
public function search($params) {
$query = BC::find()->joinWith('A', true)->joinWith('C', true);
Relation A in BC defined with "via". Dropdown filter also works.
But I still don't know how to achieve one more level deep into db structure.
This way it seems to work fully:
models/BCDSearch.php
public function search($params) {
$query = BCD::find()
->select([
'BCD.id',
'BCD.amount',
'A.id AS A_Id',
'A.name AS A_name',
'B.name AS B_name',
'B.name2 AS B_name2',
'C.name AS C_name',
'D.name AS D_name',
])
->leftJoin('BC', 'BC.id = BC_Id')
->leftJoin('B', 'B.id = B_Id')
->leftJoin('A', 'A.id = A_Id')
->leftJoin('C', 'C.id = C_Id')
->leftJoin('D', 'D.id = D_Id');
$query->andFilterWhere([
...
'A.id' => $this->A_Id,
'B.name' => $this->B_name,
'B.name2' => $this->B_name2,
'C.name' => $this->C_name,
'D.name' => $this->D_name,
]);
public function rules() {
return [
...
[[... 'A_Id', 'B_name', 'B_name2', 'C_name', 'D_name'], 'safe'],
];
}
models/base/BCD.php:
class BCD extends Whatever {
public $A_Id;
public $A_name;
public $B_name;
public $B_name2;
public $C_name;
public $D_name;
views/BCD/index.php:
GridView::widget([
'layout' => '{summary}{pager}{items}{pager}',
'dataProvider' => $dataProvider,
'pager' => [
'class' => yii\widgets\LinkPager::className(),
'firstPageLabel' => Yii::t('app', 'First'),
'lastPageLabel' => Yii::t('app', 'Last')],
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'A_Id',
'value' => 'A_name',
'filter' => yii\helpers\ArrayHelper::map(app\models\A::find()->all(), 'id', 'name')
],
[
'attribute' => 'B_name',
'value' => 'B_name',
'filter' => yii\helpers\ArrayHelper::map([['id' => 'name1', 'name' => 'name1'], ['id' => 'name2', 'name' => 'name2']], 'id', 'name')
],
[
'attribute' => 'B_name2',
'value' => 'B_name2',
'filter' => yii\helpers\ArrayHelper::map([['id' => 'name2_1', 'name' => 'name2_1'], ['id' => 'name2_2', 'name' => 'name2_2']], 'id', 'name')
],
[
'attribute' => 'C_name',
'value' => 'C_name',
'filter' => yii\helpers\ArrayHelper::map(app\models\C::find()->all(), 'C_name', 'C_name')
],
[
'attribute' => 'D_name',
'value' => 'D_name',
'filter' => yii\helpers\ArrayHelper::map(app\models\D::find()->all(), 'D_name', 'D_name')
],
'amount',
...
hope this helps others. It was not easy to figure out (at least for me) this basically easy solution. I don't know why but so far I couldn't find any relevant info on the web like this.
What I am actually doing is, fetching a list of companies from the database and passing that to the form SELECT element.
So I created a Model file, which returns an array
//=== return an array of $ID => $name of companies to use in dropdown in reports form
public function getTotalResult($table, $type, $id) {
$this->table = $table;
$select = new Select();
$spec = new Where();
$spec->equalTo('status', 1);
if ($type == 'name') {
$spec->equalTo('id', $id);
}
$select->from($this->table);
$select->where($spec);
$resultSet = $this->selectWith($select);
//$resultSet->buffer();
return $resultSet;
}
public function resultList($table){
$results = $this->getTotalResult($table, '', '');
foreach ($results as $result) {
$this->id[] = $result->id;
$this->name[] = $result->name;
}
$result = array_combine($this->id, $this->name);
return $result;
}
Then I tested this in my Controller, which returned exactly what I wanted:
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use SpangelLogin\Model\Register; // <-- Add this import
use SpangelLogin\Model\companyList; // <-- Add this import
class RegisterController extends AbstractActionController
{
protected $registerTable;
protected $companyList;
public function getcompanyList()
{
if (!$this->companyList) {
$sm = $this->getServiceLocator();
$this->companyList = $sm->get('SpangelLogin\Model\companyList');
}
return $this->companyList;
}
public function indexAction()
{
//== get list of companies
$company_table = 'rs_company';
$sector_table = 'rs_sector';
$companiesList = $this->getcompanyList()->getName($company_table, 2);
}
}
So now I want this companiesList array passed in my form's Select Element. How can I achieve that. Here is my form in which I am using select.
use Zend\Form\Form;
use Zend\Form\Element;
class SectorReportForm extends Form
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('sectorreport');
$companiesArray = $this->companiesList();
$sectorsArray = $this->sectorsList();
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/form-data');
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'company',
'attributes' => array(
'id' => 'company',
'multiple' => true,
'options' => $companiesArray,
),
'options' => array(
'label' => 'Company',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Upload',
'id' => 'submitbutton',
'class' => 'button violet right'
),
));
}
}
From a Design-Perspective, the best approach would be to handle this via Dependency-Injection. That sneaky little buzzword that confuses people so much, but actually is nothing more but to forward data between objects :P
General Dependency-Injection for Forms can be seen looking at the following answer, as well as my Blog article
How to get data from different model for select?
Zend\Form\Element\Select and Database-Values
If you do not want to go this approach, you can handle this at the Controller level, too.
$form = new My\Form();
$select = $form->get('selectCountries');
$model = new My\Countries();
$listData = $model->getCountriesAsArray();
$select->setValueOptions($listData);
I still advise you to go the different approach ;) Keeps the controllers more clean, too, which is always a good thing. Separation of concern!
Here’s my widget in the Form.Class:
$this->widgetSchema['schools'] = new sfWidgetFormChoice(array(
'choices' => Doctrine_Core::getTable('school')->getUsersSchools($userId),
'renderer_class' => 'sfWidgetFormSelectDoubleList',
'renderer_options' => array(
'label_unassociated' => 'Unassociated',
'label_associated' => 'Associated'
)));
The above works just fine, but the values that are stored are unassociated to the choices list referenced above. I need to store the ids of the array retrieved as the values. Instead, the list that is retrieved is chronological and the ids are ignored.
Here's the schoolTable query:
public function getUsersSchools($id){
$q =Doctrine_Query::create()
->select('id')
->from('school')
->where('user_id = ?', $id)
->execute();
return $q;
}
If I understand your question correctly you would like to store associated school ids.
Use the sfWidgetFormDoctrineChoice widget instead and it will work out of the box, as it using primary keys as ids.
$query = Doctrine_Core::getTable('school')->queryForSelect($userId);
$this->setWidget('schools', new sfWidgetFormDoctrineChoice(array(
'model' => 'school',
'query' => $query,
'multiple' => true,
'renderer_class' => 'sfWidgetFormSelectDoubleList',
'renderer_options' => array(
'label_unassociated' => 'Unassociated',
'label_associated' => 'Associated'
),
)));
$this->setValidator('schools', new sfValidatorDoctrineChoice(array(
'model' => 'schoool',
'query' => $query,
'multiple' => true,
)));
// in SchoolTable class
public function queryForSelect($userId)
{
return $this->createQuery('s')
->andWhere('s.user_id = ?', $userId)
;
}
If you has a proper schema (I presume the schools should be a many-to-many association), then the current from should has a schools_list field (properly defined in the generated base from) and then you can modify that field to be rendered by sfWidgetFormSelectDoubleList:
$this->widgetSchema['schools_list']->setOption('renderer_class', 'sfWidgetFormSelectDoubleList');
$this->widgetSchema['schools_list']->setOption('renderer_options', array(
'label_unassociated' => 'Unassociated',
'label_associated' => 'Associated'
));
I been trying all day to do multiple insert for the same form based on a number and i couldn't go any where with it. I hope someone here would be help me out...
am using admin generator on 1.4 doctrine. I have a form which i generated with only two fields. what am trying to do is, based on a number inserted the form will be repeated x number of times.
In the generator file I added a partial which placed a text field in the beginning of the form with default value of 1. If I choose 2 the form below gets duplicated twice..
Here is what i did to my form.. In action
class prizesActions extends autoPrizesActions
{
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm(null, array('n' => 5));
$this->prizes = $this->form->getObject();
}
}
and in the PrizesForm, I wrote the following
class PrizesForm extends BasePrizesForm
{
public function configure()
{
$array = $this->getOptions();
for ($i = 0; $i < $array['n']; $i++) {
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'prize_no' => new sfWidgetFormInputText(),
'prize' => new sfWidgetFormInputText(),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorDoctrineChoice(array('model' => $this->getModelName(), 'column' => 'id', 'required' => false)),
'prize_no' => new sfValidatorInteger(array('required' => false)),
'prize' => new sfValidatorString(array('max_length' => 200, 'required' => false)),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('prizes['.$i.'][%s]');
$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);
}
unset( $this['updated_at'],
$this['created_at']
);
}
}
I think the loop is working but its over writing the widgets at every entry and i cannot find other method to append instead. Any ideas?
Thanks,
Did you try embedForm()? Code below should give you an idea.
class PrizesForm extends BasePrizesForm
{
public function configure()
{
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'prize_no' => new sfWidgetFormInputText(),
'prize' => new sfWidgetFormInputText(),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorDoctrineChoice(array('model' => $this->getModelName(), 'column' => 'id', 'required' => false)),
'prize_no' => new sfValidatorInteger(array('required' => false)),
'prize' => new sfValidatorString(array('max_length' => 200, 'required' => false)),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('prizes[%s]');
}
}
class PrizesGroupForm extends sfForm
{
public function configure()
{
$array = $this->getOptions();
for ($i = 0; $i < $array['n']; $i++)
{
$this->embedForm('prizes_' . $i, new PrizesForm());
}
$this->widgetSchema->setNameFormat('prizes_group[%s]');
}
}
Does anyone know how to sort the MVCContrib grid when using a complex object.
My grid is displaying a list of Person and I'm trying to sort on the Country property. The problem is that Country is a property an Address class which is a property of Person.
Person.Address.Country
<%Html.Grid(Model).Columns(column =>
{
column.For(x => x.Id);
column.For(x => x.FirstName);
column.For(x => x.LastName).Sortable(false);
column.For(x => x.Address.Country).Sortable(false);
column.For(x => x.Age).Sortable(true);
}).Render(); %>
Exception:
Property 'Country' is not defined for type '{Namespace}.Person'
var sourceProp = Expression.Property(sourceParam, this.SortBy);
\MVCContrib\UI\Grid\Sortable\ComparableSortList.cs Line: 41
Any suggestions would be helpful.
Thank you,
MG1
A workaround would be to expose Country as a property on Person and use that:
public string Country { get { return Address.Country; } }
#orip gave you an answer.
But if you want to use the sorting feature you need to use:
<%Html.Grid(Model).Columns(column =>
{
column.For(x => x.Id);
column.For(x => x.FirstName);
column.For(x => x.LastName).Sortable(false);
column.For(x => x.Address.Country).Sortable(false);
column.For(x => x.Age).Sortable(true);
}).RenderUsing(new SortableHtmlTableGridRenderer<Person>())
.Render(); %>
Source: http://www.jeremyskinner.co.uk/2009/02/23/rewriting-the-mvccontrib-grid-part-3-gridmodels-and-gridrenderers/
You need to use SortColumnName for this.
column.For(x => x.Address.Country).SortColumnName("Address.Country");
I have tested this and it works like a charm :)
If you are not able to access SortColumnName(), you can get the latest version of MVC contrib from
http://mvccontrib.codeplex.com/SourceControl/changeset/changes/7db1cecc938f