2amigos/yii2-date-picker-widget year validation - yii2

I am using 2amigos/yii2-date-picker-widget.After selecting the date, user can edit the year which can be invalid like 10/30/0233. (m/d/Y)
I want to restrict user to enter invalid year, or to allow the date between specific year range only. without making the datefield readonly. How can I achieve this.
echo $form->field($model, 'date')->widget(DatePicker::className(), [
'addon' => '<i class="fa fa-calendar" aria-hidden="true"></i>',
'class' => 'form-control',
'clientOptions' => [
'autoclose' => true,
'format' => Yii::$app->params['dateFormat'],
],
]);

I have solved it using custom validator as follows
add rule for date as follows
['my_date', 'validYear'], // custom validator to check year range
Then define custom rule as follows
public function validYear($attribute, $params) {
$date = DateTime::createFromFormat("m/d/Y", $this->my_date);
$year = $date->format("Y");
$year_range = range(date('Y'), date('Y')+20, 1);
if(!empty($year) && !in_array($year, $year_range)){
$this->addError($attribute,"Please select valid date");
}
}
Though it works perfectly for me, If anyone has more appropriate solution, please suggest.

Related

Yii2: Format currency with thousands separator

I use PhpOffice\PhpWord for my Yii2 and Microsoft word 2016.
value data type decimal(15,2) when i download microsoftwordfile.docx from my project field value decimal show 10000.00 but i need 10,000.00
how to config/coding them to show 10,000.00
here myController.php
public function actionWord($id)
{
Settings::setTempDir(Yii::getAlias('#webroot').'/temp/');
$templateProcessor = new TemplateProcessor(Yii::getAlias('#webroot').'/path/to/microsoftwordfile.docx');
$model = Dataexample::findOne($id);
$templateProcessor->setValue(
[
'amount',
],
[
$model->amount,
]);
$templateProcessor->saveAs(Yii::getAlias('#webroot').'/path/to/microsoftwordfile.docx');
echo Html::a('download', Url::to(Yii::getAlias('#web').'/path/to/microsoftwordfile.docx'), ['class' => 'btn btn-danger']);
}
Well you can use yii\i18n\Formatter to format the currency and it provides you the
thousandSeparator : The character displayed as the thousands
separator (also called grouping separator) character when formatting a
number.
Go to your common\config\main.php if you are using app-advanced or the app/config/main.php if app-basic nad add the following under components array.
'formatter' => [
'thousandSeparator' => ',',
'currencyCode' => 'USD',
],
Now you can format any given number like below
Yii::$app->formatter->asCurrency(100.25);
//will output
$100.25
Yii::$app->formatter->asCurrency(1000.25);
//will output
$1,000.25
Yii::$app->formatter->asCurrency(100000.25);
//will output
$100,000.25
You should change your function like below
public function actionWord($id)
{
Settings::setTempDir(Yii::getAlias('#webroot').'/temp/');
$templateProcessor = new TemplateProcessor(Yii::getAlias('#webroot').'/path/to/microsoftwordfile.docx');
$model = Dataexample::findOne($id);
$templateProcessor->setValue(
[
'amount',
],
[
Yii::$app->formatter->asCurrency($model->amount),
]);
$templateProcessor->saveAs(Yii::getAlias('#webroot').'/path/to/microsoftwordfile.docx');
echo Html::a('download', Url::to(Yii::getAlias('#web').'/path/to/microsoftwordfile.docx'), ['class' => 'btn btn-danger']);
}
Hope this helps.

Yii2 Kartik DateTimePicker start date from far future

I'd like to ask you for support. The yii2 Kartik datetimepicker extension is giving strange and random start date. After clicking the calendar the start date might be year 4678 or year 3599. Imagine scrolling this back to 2017 :) Have you ever faced similar issues. I have checked the forum and I was not able to find the answer.
Here is my code.
<?php
// Usage with model (with no default initial value)
echo $form->field($date, 'start_time')->widget(
DateTimePicker::className(),
[
'size'=>'md',
'type' => DateTimePicker::TYPE_COMPONENT_APPEND,
'convertFormat' => true,
'pluginOptions' => [
'todayHighlight' => true,
'todayBtn' => true,
'format' => 'dd-M-yyyy H:i',
'autoclose' => true,
'startDate'=> date('d-m-Y H:i', strtotime(time())),
],
])->label('Start Date');
?>
Here is the screen shot of the datetimepicker: https://ibb.co/fu5WFw
The second parameter of date() is a timestamp, so remove strtotime():
'startDate'=> date('d-m-Y H:i',time()),

Yii2 Boostrap period picker

I'm using Yii2 and I would like to have the following things:
A dropdown with "ranges" as items (week, full week, week-end, etc.) (done)
A RangePicker that based on the chosen range will:
allow only some days as the startDate (ex: week = mondays; week-end = saturdays)
will automatically select the end date based on the start date (ex: week: monday to friday; full week: monday to sunday; week-end: saturday to sunday, etc.)
For the dropdown, it's fine. But for the DateRangePicker, I started using Krajee DateRangePicker and
I do not find a callback for "onStartDateSelected" (I only saw a callback that is called when the range is selected, but not for individual start date or end date)
I do not see a "setStopDate" function
I do not see how to dynamically change the available days of weeks
I see that we can have a set of predefined periods like "last 7 days", "this month", etc. But this is not what I want. I want the user be able to select the start date but automatically calculate the end date based on what the user has set.
(I'm new in web dev, so please if you find anything that I could improve in my question, don't be afraid to share it)
You can use the following to accomplish the task:
Use the following library:
use kartik\date\DatePicker;
Use the following code in view:
<?php
$layout3 = '<span class="input-group-addon">From Date</span>
{input1}
<span class="input-group-addon">To Date</span>
{input2}
<span class="input-group-addon kv-date-remove">
<i class="glyphicon glyphicon-remove"></i>
</span>';
$previousDay = strtotime('-7 day', strtotime(date('d-M-Y'))); //Set as per your requirement[![enter image description here][1]][1]
echo DatePicker::widget([
'type' => DatePicker::TYPE_RANGE,
'name' => 'startDate',
'value' => date('d-M-Y', $previousDay),
'name2' => 'endDate',
'value2' => date('d-M-Y'),
'separator' => '<i class="glyphicon glyphicon-resize-horizontal"></i>',
'layout' => $layout3,
'pluginOptions' => [
'autoclose' => true,
'format' => 'dd-M-yyyy'
]
]);
?>

Yii2: How to format input number to currency es-AR?

I have a _form.php file with this field:
<?=
$form->field($model, 'price')
->textInput([
'class' => 'form-control',
'type' => 'number'
])
?>
The price has this format 1234.50. I would like to have the format es-AR, like this: 1234,50.
In the GridView of index.php I use this code and it works great so I would like to do the same in the _form but it is not working.
[
'attribute' => 'price',
'value' => function($myModel) {
$myFormat = new NumberFormatter("es-AR", NumberFormatter::CURRENCY);
return $myFormat->formatCurrency($myModel->price, "ARS");
},
]
There are 2 ways to do that:
Add extra class to the price field and use javascript to convert to format you want (remember to return it back on submit)
Create priceFormat() and use it on AfterFind event and remember to use priceUnFormat() to return to decimal on BeforeSave
Use:
$form->field($model, 'attr', ['inputOptions' => ['value' => Yii::$app->formatter->asDecimal($model->attr)]])

Saving data to the join table using control options in CakePHP 3.x

I learned here how one can save the data to the fields of join table CoursesMemberships while adding or editing a student in CakePHP 3.x. In order to add grades for many courses I can do this in my add and edit forms:
echo $this->Form->control('courses.0.id', ['type' => 'select', 'options' => $courses]);
echo $this->Form->control('courses.0._joinData.grade');
echo $this->Form->control('courses.1.id', ['type' => 'select', 'options' => $courses]);
echo $this->Form->control('courses.1._joinData.grade');
echo $this->Form->control('courses.2.id', ['type' => 'select', 'options' => $courses]);
echo $this->Form->control('courses.2._joinData.grade');
...
but this form:
has a fixed number of courses for each student;
requires to select the course id from the list ('type' => 'select');
adds all courses to the student record even if not attended (well, the corresponding grade field can be kept empty, but still).
Is there a way to have a simpler form, where all courses are listed and one can only checkbox the course attended and enter the corresponding grade? I found it very challenging using control...
EDIT:
After #ndm suggested a very nice method below, I implemented it in the add.ctp:
foreach ($courses as $key => $course) {
echo $this->Form->control('courses.'.$key.'.id', ['type' => 'checkbox', 'hiddenField' => false, 'value' => $key,
'label' => $key]);
echo $this->Form->control('courses.'.$key.'._joinData.grades');
}
and corrected StudentsTable.php accordingly. And it runs with no problems.
However, if I do the same in edit.ctp, the previously saved records (e.g. for 1, 3, 5 and 7 courses are now listed as 1, 2 and 3 showing the grades for former 3rd 5th and 7th courses and the form forces me to check those three boxes. I understand that the first record disappeared because my courses start with id=1 (and so does the $key in the loop) and 'courses.0.id' is thus missing, but the general problem is that the empty fields removed by beforeMarshal function are no longer recognized in edit.ctp form and I cannot find a reasonable way to edit the student's record.
There is no build in support for what you are trying to achieve, you'll have to come up with a custom solution, which will likely either require a mixture of form and marshalling logic, or JavaScript.
You could create for example a list of checkboxes, and use the id value (wich will be zero in case the checkbox isn't checked, or the ID in case it is checked) to remove unchecked entries from the submitted data before marshalling, something like this:
echo $this->Form->control('courses.0.id', [
'type' => 'checkbox',
'value' => $courses[0]->id,
'label' => $courses[0]->title
]);
echo $this->Form->control('courses.0._joinData.grade');
echo $this->Form->control('courses.1.id', [
'type' => 'checkbox',
'value' => $courses[1]->id,
'label' => $courses[1]->title
]);
echo $this->Form->control('courses.1._joinData.grade');
// ...
// in the `StudentsTable` class
public function beforeMarshal(\Cake\Event\Event $event, \ArrayObject $data, \ArrayObject $options)
{
forach ($data['courses'] as $key => $course) {
if (empty($course['id'])) {
unset($data['courses'][$key])
}
}
}
Alternatively you could use JavaScript to disable the controls related to the checkbox so that they aren't being submitted in the first place. For this to work properly you'll need to make sure that you disable the hidden field that is by default being generated for checkboxes (see the hiddenField option), as otherwise zero will be sent for unchecked checkboxes.
Here's a quick, untested jQuery example to illustrate the principle:
echo $this->Form->control('courses.0.id', [
'class' => 'course-checkbox',
'data-join-data-input' => '#course-join-data-0',
'type' => 'checkbox',
'hiddenField' => false, // no fallback, unchecked boxes aren't being submitted
'value' => $courses[0]->id,
'label' => $courses[0]->title
]);
echo $this->Form->control('courses.0._joinData.grade', [
'id' => 'course-join-data-0',
'disabled' => true
]);
// ...
$('.course-checkbox').each(function () {
var $checkbox = $(this);
var $joinDataInput = $($checkbox.data('join-data-input'));
$checkbox.on('change', function () {
$joinDataInput.prop('disabled', !$checkbox.prop('checked'));
});
});
See also
Cookbook > Database Access & ORM > Saving Data > Modifying Request Data Before Building Entities
Cookbook > Views > Helpers > Form > Creating Select, Checkbox and Radio Controls > Options for Control
Cookbook > Views > Helpers > Form > Creating Select, Checkbox and Radio Controls > Creating Checkboxes