Yii2 adding multiple images in detail view - image

In yii2 Detail how can I add multiple images. I am able to add only 1 image for a single row.. I want multiple images to be displayed.
[
'label' => 'Class Labels',
'format' => 'image',
'value' =>'/web/images/transport_class/class8.jpg'.','.'/web/images/transport_class/class8.jpg',
],
As of now tried this not working... But if leave with single image it works. any solution for this problem??
Thanks..

'value' => function($model){
return Html::img('img1.png').Html::img('img2.png');
},
'format'=>'raw',

just use a function returning a string value, or a static helper. for images you could return:
'value' => \yii\helpers\Html::img(['#web/images/transport_class8.jpg'])
or
class MyHelper
{
public static function myImages($model)
{
$images = [];
if ($model->class1)
$images[] = Html::img('class1.jpg');
if ($model->class8)
$images[] = Html::img('class8.jpg');
return implode(' ', $images); // return your html code here
}
}
'value' => MyHelper::myImages($model)

Related

Yii2 Kartik editable input value will only change after refresh

I use Kartik Editable input widget. I have a home model and tema model attribute here. Whenever I input and submit value in the field, the value won't change on-the spot but will only change after I refresh the page instead. What should I do? Thanks!
My controller :
public function actionIndex()
{
$searchModel = new HomeSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// table only has one row
$model= Home::find()->one();
// Check if there is an Editable ajax request
if (isset($_POST['hasEditable'])) {
// use Yii's response format to encode output as JSON
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
// read your posted model attributes
if ($model->load($_POST)) {
// read or convert your posted information. Based on the 'name' property set in the view. So this 'tema' of $model-> tema comes from 'name' property set in the view.
$value = $model->tema;
$model->save();
// return JSON encoded output in the below format
return ['output'=>$value, 'message'=>'output berhasil'];
// alternatively you can return a validation error
// return ['output'=>'', 'message'=>'Validation error'];
}
// else if nothing to do always return an empty JSON encoded output
else {
return ['output'=>'', 'message'=>'output gagal'];
}
};
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'model'=>$model,
]);
}
The view
<?php
echo Editable::widget([
'model' => $model,
'attribute' => 'tema',
'value'=>$model->tema,
/*'asPopover'=>'false',*/
'type' => 'post',
'header'=>'tema',
'valueIfNull'=>'value-nya NULL',
'format'=>'link',
'size'=> 'lg',
'inputType' => Editable::INPUT_TEXT,
'editableValueOptions' => ['class' => 'text-success h3']
]); ?>
Another issue, whenever I used 'asPopover'=>'false', it shows no error but nothing happen when I click the supposedly editable-input field. The editable-inline field just won't show up. When I use the popOver option,the pop-up just automatically triggered without clicking and also it pop-up on the top left corner of the page. Only after I clicked on the editable widget that triggered the pop-up will it recorrect itself to the proper position. Is it a bug? I used the latest Yii2 with bootstrap 4, and I had set the global parameter in params.php config with 'bsVersion' => '4.x', as in the documentation
In the Controller, try this:
public function actionIndex()
{
$searchModel = new HomeSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if (isset($_POST['hasEditable']))
{
$tema = Yii::$app->request->post('editableKey');
$modelHome = Home::findOne($tema);
$posted = current($_POST['Home']);
$post = ['Home' => $posted];
if ($modelHome->load($post)) {
$modelHome->save();
$out = Json::encode(['output'=>$modelHome->tema, 'message'=>'']);
return $out;
}
return;
};
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'model'=>$model,
]);
}

Yii2: Labels with attributeLabels() in GridView

I generated a model which got the function attributeLabels():
public function attributeLabels()
{
return [
'column1' => 'name of column1',
'column2' => 'name of column2',
];
}
I also wrote a function in my model, which creates a SQL statement:
public function getReminders()
{
$sql = Yii::$app->db->createCommand('SELECT
[[column1]], [[column2]]
FROM {{%table}}
WHERE column1 > :value')
-> bindValue(':value', '10');
return $sql;
}
Then I created a controller with an actionIndex() function:
public function actionIndex()
{
$model = new TestObject();
$testmodels= new ActiveDataProvider([
'models' => $model->getReminders()->queryAll(),
]);
return $this->render('index', [
'testmodels' => $testmodels,
]);
Finally I created a View
GridView::widget([
'dataProvider' => $testmodels,
'layout' => '{items}{pager}',
'columns' => ['column1', column2,],
]);
For some reason, the column names of the resulting page are column1 and column2. I have tried to create a listView and got the correct names for the columns. How comes that the column names are not set by attributeLabels() in this case? I really don't want to use the label property in the gridview.
Thank you in advance!
If you just need to change the label for the gridview column you can configure you column with proper value for label
GridView::widget([
'dataProvider' => $testmodels,
'layout' => '{items}{pager}',
'columns' => [
[
'attribute'=> column1',
'label' => 'Your Lable for column 1',
],
[
'attribute'=> column2',
'label' => 'Your Lable for column 2',
],
],
]);
you can take a look at thsi for a brief guide http://www.yiiframework.com/doc-2.0/guide-output-data-widgets.html or here for ref http://www.yiiframework.com/doc-2.0/yii-grid-datacolumn.html
I would do the following:
make the base class for the TestObject be ActiveRecord
Override tableName
Simplify getReminders, make it static, and let it return Query not a Command
class TestModel extends yii\db\ActiveRecord
{
...
public static function tableName()
{
return {{%table}};
}
public static function getReminders()
{
$query = self::find()->select(['column1', 'column2'])
->where(['>', 'column1', 10]);
return $query;
}
...
}
Don't set models of the ActiveDataProvier and instead set query
'models' => TestObject::getReminders(),

Drupal 8, add an image field from a BuildForm with preview

I created a custom form from a buildForm function. In this form, I would like add an image field.
I can do that via this code :
$form['main']['image'] = array(
'#type' => 'text_format',
'#title' => t('image'),
'#default_value' => array(10),
);
I can upload and remove the image from my form. However, when I upload an image, I haven't this preview.
I mean, when I create a content via the Drupal UI. I can add a preconfigured "image" field. When I upload an image via this "image" field, I have a preview of the image.
And here, when I create the field element programmatically, I haven't a preview of the image when I upload her.
How use api Drupal for have the preview of the image when I upload her via my "image" field ?
So here is how I got my form to display the thumbnail image. What I basically did was take the code in ImageWidget::process, put it in a theme preprocessor and set the #theme property of the element to image_widget.
Your image element in your form class should look like this:
$form['profile_image'] = [
'#type' => 'managed_file',
'#title' => t('Profile Picture'),
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
'file_validate_size' => array(25600000),
),
**'#theme' => 'image_widget',**
**'#preview_image_style' => 'medium',**
'#upload_location' => 'public://profile-pictures',
'#required' => TRUE,
];
In your name_of_default_theme.theme file, you need the following:
function name_of_default_theme_preprocess_image_widget(&$variables) {
$element = $variables['element'];
$variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));
if (!empty($element['fids']['#value'])) {
$file = reset($element['#files']);
$element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
$file_variables = array(
'style_name' => $element['#preview_image_style'],
'uri' => $file->getFileUri(),
);
// Determine image dimensions.
if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
$file_variables['width'] = $element['#value']['width'];
$file_variables['height'] = $element['#value']['height'];
} else {
$image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->isValid()) {
$file_variables['width'] = $image->getWidth();
$file_variables['height'] = $image->getHeight();
}
else {
$file_variables['width'] = $file_variables['height'] = NULL;
}
}
$element['preview'] = array(
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $file_variables['width'],
'#height' => $file_variables['height'],
'#style_name' => $file_variables['style_name'],
'#uri' => $file_variables['uri'],
);
// Store the dimensions in the form so the file doesn't have to be
// accessed again. This is important for remote files.
$element['width'] = array(
'#type' => 'hidden',
'#value' => $file_variables['width'],
);
$element['height'] = array(
'#type' => 'hidden',
'#value' => $file_variables['height'],
);
}
$variables['data'] = array();
foreach (Element::children($element) as $child) {
$variables['data'][$child] = $element[$child];
}
}
The solution works well, but the image module is missing a class with the #FormElement annotation. That is why the element isn't rendered properly.
Can you try with managed_file type?
'#type' => 'managed_file'
First some clarifications, then some observations and then the code that worked for me.
Clarifications:
I am using Drupal 9, but the solution proposed below may work also
for Drupal 8.
By "default temporary directory" I mean the directory defined in the following settings
file
[DrupalrootDirectory]/sites/default/settings.php
using
$settings['file_temp_path'] = '/The/Absolute/Path/To/Your/Temporary/Directory';
or defined by your operating system and that you can check in the Drupal administration
menu:
[YourSiteWebAddress]/admin/config/media/file-system
When something is written inside square brackets [], like this, [DrupalrootDirectory], it means that you should replace it with the actual name in your system, Drupal installation or custom module/file, without the square brackets.
Observations:
No need to create apriory any directories inside the default
temporary directory. They will be created automatically.
When you click the Remove button (which appears automatically as soon you upload a file in your custom form), the image file and the thumbnail files are deleted from the default temporary directory.
It does not matter if you define your default temporary directory inside the default site location,
e.g.,
[DrupalrootDirectory]/sites/default/files
or outside it, for example,
[DrupalrootDirectory]/sites/other/files
In my case, I defined it to:
[DrupalrootDirectory]/sites/default/files/temp
No need to make changes to .htaccess (in Apache server) file inside the default temporary directory or
inside
[DrupalrootDirectory]/sites/default/files
Keep them as Drupal created them.
No need to change file rights to "777" inside the default temporary directory, "775" is enough.
Check that your Drupal installation is actually creating thumbnails for images uploaded using the
administration menu:
[YourSiteWebAddress]/admin/content/media
when using the "Add media" link
[YourSiteWebAddress]/media/add/image
No need to make any other changes inside Drupal settings
file
[DrupalrootDirectory]/sites/default/settings.php
The code that worked for me:
It is based in the code published above by "Je Suis Alrick". But his code was not working for me, neither I could not find in which part of his code the thumbnail image was actually created. In my search for it, this post helped
Generate programmatically image styles with Drupal 8
So, here is the final code:
In your custom module, in the custom form file, most probably located
in:
[DrupalrootDirectory]/modules/custom/[NameOfYourCustomModule]/src/Form/[NameOfYourCustomForm].php
you add the element to be used to upload the image file:
$form['profile_image'] = [
'#type' => 'managed_file',
'#title' => t('Profile Picture'),
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
'file_validate_size' => array(25600000),
),
'#theme' => 'image_widget',
'#preview_image_style' => 'medium',
'#required' => TRUE,
];
So far, the same as in the code of "Je Suis Alrick", except that I deleted the definition for '#upload_location', so the default temporary directory will be used avoiding security complaints.
The important part here is:
the definition of the '#theme', which may be any name, but in this case is 'image_widget';
and the definition of '#preview_image_style', which must be the machine name of one of the image styles defined in your Drupal installation, that you can check in your administration
menu
[YourSiteWebAddress]/admin/config/media/image-styles
For this case the style 'medium' will be used, which is one of the image styles created by default by Drupal.
Then, in the module file of your custom module, named [NameOfYourCustomModule].module and most probably located
in:
[DrupalrootDirectory]/modules/custom/[NameOfYourCustomModule]/
You will need to paste the following code:
<?php
use Drupal\image\Entity\ImageStyle;
function [NameOfYourCustomModule]_preprocess_image_widget(&$variables) {
$element = $variables['element'];
$variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));
if (!empty($element['fids']['#value'])) {
$file = reset($element['#files']);
$element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
$file_variables = array(
'style_name' => $element['#preview_image_style'],
'uri' => $file->getFileUri(),
);
// Determine image dimensions.
if (isset($element['width']['#value']) && isset($element['height']['#value'])) {
$file_variables['width'] = $element['width']['#value'];
$file_variables['height'] = $element['height']['#value'];
} else {
$image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->isValid()) {
$file_variables['width'] = $image->getWidth();
$file_variables['height'] = $image->getHeight();
$style = ImageStyle::load($file_variables['style_name']);
$image_uri = $file->getFileUri();
$destination = $style->buildUri($image_uri);
$style->createDerivative($image_uri, $destination);
}
else {
$file_variables['width'] = $file_variables['height'] = NULL;
}
}
$element['preview'] = array(
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $file_variables['width'],
'#height' => $file_variables['height'],
'#style_name' => $file_variables['style_name'],
'#uri' => $file_variables['uri'],
);
// Store the dimensions in the form so the file doesn't have to be
// accessed again. This is important for remote files.
$element['width'] = array(
'#type' => 'hidden',
'#value' => $file_variables['width'],
);
$element['height'] = array(
'#type' => 'hidden',
'#value' => $file_variables['height'],
);
}
$variables['data'] = array();
foreach (\Drupal\Core\Render\Element::children($element) as $child) {
$variables['data'][$child] = $element[$child];
}
}
You should note that at the end of the name of the function, the name of the theme 'image_widget' is included, which tells Drupal to process your form element using the defined above function: [NameOfYourCustomModule]_preprocess_image_widget
What have I added?
The line at the
top:
use Drupal\image\Entity\ImageStyle;
And the following four lines that actually create the image thumbnail inside the default temporary directory:
$style = ImageStyle::load($file_variables['style_name']);
$image_uri = $file->getFileUri();
$destination = $style->buildUri($image_uri);
$style->createDerivative($image_uri, $destination);
With the addition of these five lines I got it working!
Nice answer thx, only change I'd make is using Token for upload location, and Bytes for file upload size, although I guess it depends on the use case. Something like the below (stripped out most of the code for the sake of simplicity.
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\Utility\Token;
class SampleForm extends FormBase
{
protected $currentUser;
protected $token;
public function __construct(AccountProxyInterface $current_user, Token $token) {
$this->currentUser = $current_user;
$this->token = $token;
}
public static function create(ContainerInterface $container)
{
return new static(
$container->get('current_user'),
$container->get('token')
);
}
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['sample_image'] = [
'#type' => 'managed_file',
'#title' => t('Profile Picture'),
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
'file_validate_size' => file_upload_max_size() / pow(Bytes::KILOBYTE, 2) . 'M',
),
'#theme' => 'image_widget',
'#preview_image_style' => 'medium',
'#upload_location' => $this->token->replace('private://[date:custom:Y]-[date:custom:m]'),
'#required' => TRUE,
];
return $form;
}
}
Theme it to work on multiple file upload. From #Je Suis Alrick answer above.
function themename_preprocess_image_widget(&$variables) {
$element = $variables['element'];
$variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));
if (!empty($element['fids']['#value'])) {
foreach ($element['#files'] as $file) {
$element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
$file_variables = array(
'style_name' => $element['#preview_image_style'],
'uri' => $file->getFileUri(),
);
// Determine image dimensions.
if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
$file_variables['width'] = $element['#value']['width'];
$file_variables['height'] = $element['#value']['height'];
} else {
$image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->isValid()) {
$file_variables['width'] = $image->getWidth();
$file_variables['height'] = $image->getHeight();
}
else {
$file_variables['width'] = $file_variables['height'] = NULL;
}
}
$element['preview'][] = array(
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $file_variables['width'],
'#height' => $file_variables['height'],
'#style_name' => $file_variables['style_name'],
'#uri' => $file_variables['uri'],
);
// Store the dimensions in the form so the file doesn't have to be
// accessed again. This is important for remote files.
$element['width'] = array(
'#type' => 'hidden',
'#value' => $file_variables['width'],
);
$element['height'] = array(
'#type' => 'hidden',
'#value' => $file_variables['height'],
);
}
}
$variables['data'] = array();
foreach (\Drupal\Core\Render\Element::children($element) as $child) {
$variables['data'][$child] = $element[$child];
}
}

Drupal 7 Form Api AJAX callback on a text_format field do not work

I have a field in my custom module
$form['update']['text'] = array(
//'#type' => 'textarea',
'#type' => 'text_format',
If I use textarea everything is ok, but if I use the text_format my ajax callback do not change the value of the field.
function maintenance_update_form_update_callback($form, $form_state) {
$entry = $form_state['entries'][$form_state['values']['aid']];
// Setting the #value of items is the only way I was able to figure out
// to get replaced defaults on these items. #default_value will not do it
// and shouldn't.
dpm($form_state['entries'][$form_state['values']['aid']]);
foreach (array('status', 'type', 'title', 'text', 'name', 'email', 'phone', 'notes') as $item) {
$form['update'][$item]['#value'] = $entry->$item;
//dpm($entry);
//drupal_set_message("entry->item értéke: ENTRY: $entry, ITEM: $item , $entry->$item");
}
What can be wrong with this field? As far as I know this type also support ajax requests....
//drupal_set_message('Callback $form');
dpm($form);
return $form;
}
It's very simple - text_format is not simple single-value form item. It takes array for value param like that
array(
'value' => '123123',
'format' => 'html'
)
So, if you want to change value try this code:
$form['update']['text']['value']['#value'] = 'text message';
Adding to #ProFak solution, if you want to change the format of the text_format field type, you will have to use the following code
$altered_text_format = 'full_html';
$form['update']['format']['format']['#value'] = $altered_text_format;
So the complete solutions will look like as below
function maintenance_update_form_update_callback($form, $form_state) {
$entry = $form_state['entries'][$form_state['values']['aid']];
// Setting the #value of items is the only way I was able to figure out
// to get replaced defaults on these items. #default_value will not do it
// and shouldn't.
foreach (array('status', 'type', 'title', 'text', 'name', 'email', 'phone', 'notes') as $item) {
// #ProFak Solution
$form['update']['text']['value']['#value'] = $entry->$item;
// #Sukhjinder Singh additional solution
$altered_text_format = 'full_html';
$form['update']['format']['format']['#value'] = $altered_text_format;
}
return $form['update'];
}

Symfony 1.4 sfWidgetFormInputFileEditable customization

I am using the sfWidgetFormInputFileEditable widget for my users to upload images.
I would like to see if there's a way to alter the way it works be default. When a user is adding a "new" object, I would like for it to show a generic picture, and when it's an "edit" then it can show the existing pic. I tried writing a PHP conditional statement but that's not working for me because when it's a "new" item I can't pull the parameter "getPicture1" because it doesn't exist.
My widget currently:
$this->widgetSchema['picture1'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/car/'.$this->getObject()->getPicture1(),
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
You have two options (the second one is more easy).
First option: create your own sfWidgetFormInputFileEditable and extends the original.
In a file lib/widget/myWidgetFormInputFileEditable.class.php:
class myWidgetFormInputFileEditable extends sfWidgetFormInputFileEditable
{
protected function getFileAsTag($attributes)
{
if ($this->getOption('is_image'))
{
if (false !== $src = $this->getOption('file_src'))
{
// check if the given src is empty of image (like check if it has a .jpg at the end)
if ('/uploads/car/' === $src)
{
$src = '/uploads/car/default_image.jpg';
}
$this->renderTag('img', array_merge(array('src' => $src), $attributes))
}
}
else
{
return $this->getOption('file_src');
}
}
}
Then you need to call it:
$this->widgetSchema['picture1'] = new myWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/car/'.$this->getObject()->getPicture1(),
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
Second option: check if the object is new then use the default image.
$file_src = $this->getObject()->getPicture1();
if ($this->getObject()->isNew())
{
$file_src = 'default_image.jpg';
}
$this->widgetSchema['picture1'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/car/'.$file_src,
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));

Resources