Count items from relationschip in faker - laravel

I have a page model and a textblock model for creating pages with as many textblocks as needed. I'm using sortable from jquery ui to sorter the textblocks by position field in migration table. So every time I'm creating a textblock the position will count + 1, starting from 1.
Now I'm setting up 2 faker factories, 1 for pages and 1 for generating dummy textblocks on the pages. I'm wondering how I can count the amount of random textblocks the factory will create? So I can say every time the page factory creates a page with a few textblocks the counter won't override the amount of textblocks each page has.
$factory->define(Textblock::class, function (Faker $faker) {
return [
'page_id' => Page::all()->random()->id,
'title' => $faker->sentence(rand(2, 5)),
'summary' => $faker->text,
'position' => // how can i code this ?
'visible' => $faker->boolean($chanceOfGettingTrue = 50),
]; });

You could create a Counter object to keep all the incrementations in memory. Something similar to:
class Counter
{
private static $counters = [];
public static function nextCounterFor($key, $default = 0)
{
if (!isset(self::$counters[$key])) {
if (is_callable($default)) {
$default = $default();
}
if (!is_int($default)) {
throw new \RuntimeException('The default value must be an integer');
}
self::$counters[$key] = $default;
}
return ++self::$counters[$key];
}
}
Then you can use it to generate new positions for the given page ID. If there is no key defined yet, you must take the last position used for that page from the db (if it exists)
$factory->define(Textblock::class, function (Faker $faker) {
$page = Page::inRandomOrder()->first();
$position = Counter::nextCounterFor($page->id, static function() use ($page) {
$lastPosition = Textblock::where('page_id', $page->id)->orderBy('position', 'DESC')->first();
return $lastPosition !== null ? $lastPosition->position : 0;
});
return [
'page_id' => $page->id,
'title' => $faker->sentence(rand(2, 5)),
'summary' => $faker->text,
'position' => $position,
'visible' => $faker->boolean($chanceOfGettingTrue = 50),
];
});

Related

How to include a list view in the details show view sonata bundle

I'm trying to have a query result in the show view and then to normally edit/delete the rows as in the list view (pagination as well).
As far I did this:
protected function configureShowFields(ShowMapper $show): void
{
$userId = $this->getSubject()->getId();
$show
->add('lastName')
->add('firstName')
->add('phoneNumber')
->add('email')
->add('fullAddress')
->end();
$show
->with('Cars', [
'class' => 'col-md-8',
'box_class' => 'box box-solid',
])
->add('cars', null, [
'query_builder' => function (EntityRepository $er) use ($userId) {
return $er->createQueryBuilder('u')
->where('u.user = :user')
->setParameter('param', $userId);
},
'template' => 'Admin/list_items.html.twig'
])
->end();
}
but in the template I've only added a table and displayed the car items but I must be able to click on the item and go to their detail page. Any ideas what to use to achieve that?

Yii2 display multiple images in gridview row

I want to display multiple images in a gridviews single row. For example: I have table A, Table B and table C.
Table A has my_id.
In Table B my_id is the foreign key. Along with my_id it has c_id.
Table C has c_id which is in reference in Table B.
Table C also has a filepath to display images.
in Table A i have my_id as follows:
1, 2, 3, 4, 5, 6.
In Table B i have my_id as follows.
1 ,1 ,1 ,2 ,3, 3.
In Table B i also have c_id as follows.
1, 2, 3, 4, 5, 6.
In table C my c_id's are:
1, 2, 3, 4, 5, 6. and these id's have filepath associated with each of them. They are different images.
Now my gridview should display 3 different images for my_id because of the foreign key constraints. but it displays only 1 image.
My code is below:
In my model
public function getPictogramsID()
{
$pictogramsID = SdsrefGhsPictograms::find()->where(['sdsref_id' => $this->sdsref_id])->all();
foreach ($pictogramsID as $picID){
return $picID->pictogram_id;
}
}
public function getPictogramPath()
{
$pictogramsID = GhsPictogram::find()->where(['pictogram_id' => $this->getPictogramsID()])->all();
foreach ($pictogramsID as $picID){
$pic = $picID->pictogram_filepath;
}
return $pic;
}
public function getPictogramUrl()
{
//var_dump($this->getPictogramPath()); exit();
return \Yii::$app->request->BaseUrl.'/web'.$this->getPictogramPath() ;
}
my index file grid view image code
[
'label' => 'Hazards',
'format' => 'raw',
'value' => function ($data) {
return Html::img($data->getPictogramUrl(), ['alt'=>'myImage','width'=>'20','height'=>'30']);
},
],
I am also trying to add a bootstrap tool tip to this.. tool tip is displaying successfully but I think the looping is not not done in a correct way so it is repeating my images.
here is my updated gridview code.
[
'label' => 'Hazards',
'format' => 'raw',
'value' => function ($data) {
$images = '';
// append all images
foreach($data->getPictogramName() as $name)
foreach ($data->getPictogramUrl() as $url)
$images = $images.Html::img($url,['alt'=>'','width'=>'30','height'=>'30', 'data-toggle'=>'tooltip','data-placement'=>'left','title' => $name ,'style'=>'cursor:default;']);
return $images;
}
],
You have few logical errors in model and grid view. In all these areas you are dealing with one item instead of three.
In your model
public function getPictogramsID()
{
$ids = [];
$pictogramsID = SdsrefGhsPictograms::find()->where(['sdsref_id' => $this->sdsref_id])->all();
foreach ($pictogramsID as $picID){
$ids[] = $picID->pictogram_id;
}
return $ids;// returning all three ids
}
public function getPictogramPath()
{
$pic = [];
$pictogramsID = GhsPictogram::find()->where(['pictogram_id' => $this->getPictogramsID()])->all();
foreach ($pictogramsID as $picID){
$pic[] = $picID->pictogram_filepath;
}
return $pic;
}
public function getPictogramUrl()
{
$url = [];
foreach($this->getPictogramPath() as $path):
$url[] = \Yii::$app->request->BaseUrl.'/web'.$path;
endforeach;
return $url; // returning al urls
}
Now in you view loop over all urls and append images with each url
[
'label' => 'Hazards',
'format' => 'raw',
'value' => function ($data) {
$images = '';
// append all images
foreach($data->getPictogramUrl() as $url):
$images = $images.Html::img($url, ['alt'=>'myImage','width'=>'20','height'=>'30']);
endforach;
return $images;
},
],

Eager loading of related model (nested set)

In Yii1 I could load parent (nested set) using eager loading. It was like this:
class Category extends CActiveRecord {
public function relations() {
return array(
'parent' => array(self::HAS_ONE, 'Category', '', 'on' => '(t.left between parent.left and parent.right) and parent.level + 1 = t.level', 'joinType'=>'left join'),
);
}
}
(relation to the same model, setting 2nd parameter as empty, and setting my own condition in on parameter).
How can I do the same in Yii2? (cause Yii2 needs key => value pair to initialize relation).
Try use like this:
public function getRelation()
{
return $this->hasOne(RelationTable::className(), ['id' => 'relation_id'])->onCondition(array|string)->andOnCondition(array|string);
}
or
public function getRelation()
{
return $this->hasOne(RelationTable::className(), ['id' => 'relation_id'])->andOnCondition(array|string);
}
More is here:
http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#relational-data

How can I pass an object to the sheet method in Laravel Excel?

I'm trying to pass an object into the Laravel Excel sheet method but I'm not sure how. I get an undefined variable error - which I realize is because I'm inside a function that doesn't have scope to reach the object.
Ultimately I'm loading up regtypes from one table - attendees from another and creating a sheet for each regtype that contains all attendees of that type.
My code:
public function export()
{
$date = date('Y_m_d');
\Excel::create('CMO_Connect_Attendees_Export_'.$date, function($excel) {
$regtypes = Regtype::all();
foreach ($regtypes as $regtype) {
if( $regtype->attendees(3)->count() ) {
$excel->sheet('Attendees', function($sheet) {
$date = date('Y_m_d');
$attendees = new Attendee;
$atts = Attendee::where('block_id', '=', \Input::get('block_id'))
->where('regtype_id', '=', $regtype->id)
->get();
$sheet->setStyle(array(
'font' => array(
'name' => 'Arial',
'size' => 12,
'bold' => false
)
));
$sheet->loadView('attendees.export', array('atts' => $atts))->with('curdate',$date)->with('regtype_name',$regtype->name);
});
} //endif
} //endforeach
$excel->export('xls');
});
}
As I was writing this up I figured it out, pretty simple I should have known as it's simply a method of a Laravel Facade, so you pass the object as an array like so:
$excel->sheet('Attendees', array('regtype' => $regtype), function($sheet) {

How to validate a date without day with sfForm?

I'm creating a payment form with symfony 1.4 , and my form has a date widget defined like this, so that the user can select the expiration date of his credit card:
new sfWidgetFormDate(array(
'format' => '%month%/%year%',
'years' => array_combine(range(date('Y'), date('Y') + 5), range(date('Y'), date('Y') + 5))
Notice the absence of %day% in the format like in most payment forms.
Now my problem is that sfValidatorDate requires the 'day' field not to be empty. To work around this, I created a custom validator using a callback, which works well:
public function validateExpirationDate($validator, $value)
{
$value['day'] = '15';
$dateValidator = new sfValidatorDate(array(
'date_format' => '#(?P<day>\d{2})(?P<month>\d{2})(?P<year>\d{2})#',
'required' => false,
'min' => strtotime('first day of this month')));
$dateValidator->clean($value);
return $value;
}
I feel there might be a simpler way to achieve this. What do you think? Have you already solved this problem in a cleaner way?
How do you store the date? If you just store month and year as integers or strings, then you can just make 2 choice widgets. But if you store it as datetime (timestamp), then you need a valid date anyway. This means that you need to automatically assign values to 'day' (usually first or last day of the month).
class YourForm extends BaseYourForm
{
public function configure()
{
$this->widgetSchema['date'] = new sfWidgetFormDate(array(
'format' => '%month%/%year%'
));
$this->validatorSchema['date'] = new myValidatorDate(array(
'day_default' => 1
));
}
}
class myValidatorDate extends sfValidatorDate
{
protected function configure($options = array(), $messages = array())
{
$this->addOption('day_default', 1);
parent::configure($options, $messages);
}
protected function doClean($value)
{
if (!isset($value['day']))
{
$value['day'] = $this->getOption('day_default');
}
return parent::doClean($value);
}
}
There's no need to use a custom validation class: you can simply override the tainted values passed to your bind() method:
<?php
// in your form class
public function bind(array $taintedValues = null, array $taintedFiles = null)
{
$taintedValues['date']['day'] = 1;
return parent::bind($taintedValues, $taintedFiles);
}
I used simplest way, for validate credit card expiration day:
$post_data = $request->getParameter('my_form');
$post_data['card_exp']['day'] = 1; //sets the first day of the month
$this->form->bind($post_data);
Hope this helps somebody.
I solve this first in the form class
$year = range(date('Y'), date('Y') - 50);
$this->widgetSchema['date'] = new sfWidgetFormDate(array(
'format' => '%year%',
'years' => array_combine($year, $year),
'can_be_empty' => false
));
Next...
public function bind(array $taintedValues = null){
$taintedValues['date']['day'] = '01';
$taintedValues['date']['month'] = '01';
parent::bind($taintedValues);
}
The field in the database is date type DATE.

Resources