Yii2: Images stored in different tables - file-upload

Here my controller:
$model = new VehicleType();
if ($model->load(Yii::$app->request->post())) {
if($model->validate()){
$model->save();
$id = $model->id;
$model->file = UploadedFile::getInstance($model, 'file');
if($model->file){
$id = $model->id;
$imageName = "vehicletype_".$id.'_'.getdate()[0];
$model->file->saveAs('uploads/'.$imageName.'.'.$model->file->extension);
$station = VehicleType::findOne($id);
$station->image = '#web/uploads/'.$imageName.'.'.$model->file->extension;
$station->save();
}
return $this->redirect(['vehicletype/index']);
}
} else {
return $this->renderAjax('create', [
'model' => $model,
]);
}
}
My view:
<div class="row">
<div class="col-lg-5">
<?php $form = ActiveForm::begin(['id' => 'station-form', 'options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'description')->textarea() ?>
<?= $form->field($model, 'file')->fileInput() ?>
<div class="form-group">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
My model:
public function rules()
{
return [
[['description'], 'string'],
[['record_status'], 'integer'],
[['name', 'image'], 'string', 'max' => 255]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
'description' => 'Description',
'image' => 'Image',
'record_status' => 'Record Status',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getVehicles()
{
return $this->hasMany(Vehicles::className(), ['vehicle_type_id' => 'id']);
}
}
With this I can upload only one picture/post, I want one post to have multiple pictures, so I create a new table call 'Image' to stored my pictures and have a one-to-many relationship.
But I run into a problem, how can I add data to 2 tables from just 1 form
I'm using Yii2 basic template
Thanks

Step 1
First create two variables in your vehicletype model.
public $uploadedImages;
public $imageforNewtable = array();
Step 2
Make sure to mention this imageforNewtable variable in your model rule.
[['imageforNewtable'], 'image', 'extensions' => 'png, jpg, JPEG'],
Step 3
In your form:
<?php $form = ActiveForm::begin(['id' => 'station-form', 'options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->field($model, 'imageforNewtable[]')->fileInput(['accept' => 'image/*']); ?>
Step 4
In Your Controller:
$model->uploadedImages = UploadedFile::getInstances($model,'imageforNewtable');
// Make sure to put "Instances" (plural of Instance) for uploading multiple images
$model->imageforNewtable = array(); // To avoid blank entries as we have entries in $_FILES not in $_POST.
foreach ($model->uploadedImages as $singleImage)
{
$model->imageforNewtable[] = time() . '_' . $singleImage->name;
}
Now bulk insert data in Images table :
$bulkInsertforimages = array(); //defined benchInsert array for images
$columnsArray = ['blog_id', 'image']; //Column names in which bulk insert will take place.
if ($model->imageforNewtable != '') {
foreach ($model->imageforNewtable as $singleData) {
$bulkInsertforimages[] = [
'blog_id' => $model->id,
'image' => $singleData,
];
}
}
if (count($bulkInsertforimages) > 0) {
$command = \Yii::$app->db->createCommand();
$command->batchInsert('YOUR_IMAGE_TABLE', $columnsArray, $bulkInsertforimages)->execute();
}

Related

How to check if an item belongs to a hasMany association before updating in CakePHP 3.2

What I am trying to do:
I have Estimates and Estimates have items "EstimateItems". When updating a Estimate the EstimateItems changed should update. (using patchEntity)
This is working with my current code, my only problem is that other users can edit the Estimate Items of other users when changing the primary key of a EstimateItem in the edit form, because when patching the existing EstimateItems CakePHP only looks at the primary key of the EstimateItem and doesn't take the association in consideration. Also it's still possible to edit the estimate_id of a EstimateItem while $protected estimate_id is set to false.
So what I need is CakePHP to validate that this EstimateItem belongs to the current association before updating or while trying to update.
I hope some one can tell me what I am doing wrong or what I am missing.
Current Query
UPDATE
estimate_items
SET
data = 'Test Query 1',
amount = 123456789,
tax_id = 3
WHERE
id = 3
Expected Query
UPDATE
estimate_items
SET
data = 'Test Query 1',
amount = 123456789,
tax_id = 3
WHERE
id = 3 AND estimate_id = 1
Current code:
Estimates -> Edit.ctp
<?php $this->Form->templates($formTemplates['default']); ?>
<?= $this->Form->create($estimate, ['enctype' => 'multipart/form-data']) ?>
<fieldset>
<legend><?= __('Offerte') ?></legend>
<?= $this->Form->input('reference', ['label' => __('#Referentie'), 'autocomplete' => 'off']) ?>
<?= $this->Form->input('client_id',
[
'type' => 'select',
'empty' => true,
'label' => __('Klant'),
'options' => $clients
]
)
?>
<?php
foreach($estimate->estimate_items as $key => $item){
?>
<div class="item">
<legend>Item</legend>
<?= $this->Form->hidden('estimate_items.'. $key .'.id') ?>
<?= $this->Form->input('estimate_items.'. $key .'.data', ['type' => 'text', 'label' => __('Beschrijving')]) ?>
<?= $this->Form->input('estimate_items.'. $key .'.amount', ['type' => 'text', 'label' => __('Bedrag'), 'class' => 'input-date']) ?>
<?= $this->Form->input('estimate_items.'. $key .'.tax_id',
[
'type' => 'select',
'empty' => true,
'label' => __('Belasting type'),
'options' => $taxes
]
)
?>
</div>
<?php
}
?>
<legend>Informatie</legend>
<?= $this->Form->input('date', ['type' => 'text', 'label' => __('Offerte datum'), 'autocomplete' => 'off']) ?>
<?= $this->Form->input('expiration', ['type' => 'text', 'label' => __('Verloop datum'), 'autocomplete' => 'off']) ?>
</fieldset>
<?= $this->Form->button(__('Save')); ?>
<?= $this->Form->end() ?>
Estimates Controller
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class EstimatesController extends AppController
{
public function edit($id){
$associated = ['EstimateItems'];
$estimate = $this->Estimates->get($id, ['contain' => $associated]);
$this->log($estimate);
if($this->request->is(['patch', 'post', 'put'])) {
$estimate = $this->Estimates->patchEntity($estimate, $this->request->data, [
'associated' => $associated
]);
$estimate->total = '0';
$this->log($estimate);
$this->log($this->request->data);
if($this->Estimates->save($estimate, ['associated' => $associated])){
$this->Flash->success(__('De offerte is bijgewerkt'));
return $this->redirect(['action' => 'index']);
}
}
$this->set('taxes', $this->Estimates->Taxes->find('list', [ 'keyField' => 'id', 'valueField' => 'tax_name' ]));
$this->set('clients', $this->Estimates->Clients->find('list', [ 'keyField' => 'id', 'valueField' => 'companyname' ]));
$this->set('estimate', $estimate);
}
}
EstimatesTable
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\ORM\Rule\IsUnique;
class EstimatesTable extends Table
{
public function initialize(array $config)
{
$this->addAssociations([
'hasOne' => ['Taxes'],
'belongsTo' => ['Companies', 'Clients'],
'hasMany' => ['EstimateItems' => [
'foreignKey' => 'estimate_id'
]]
]);
}
public function buildRules(RulesChecker $rules){
// A Node however should in addition also always reference a Site.
$rules->add($rules->existsIn(['estimate_id'], 'EstimateItems'));
return $rules;
}
}
EstimateItem Entity
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class EstimateItem extends Entity
{
protected $_accessible = [
'*' => false,
'data' => true,
'amount' => true,
'tax_id' => true,
'unit_id' => true
];
}
EstimateItemsTable
<?php
namespace App\Model\Table;
use Cake\ORM\Entity;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\ORM\Rule\IsUnique;
use Cake\ORM\Query;
class EstimateItemsTable extends Table
{
public function initialize(array $config)
{
$this->addAssociations([
'belongsTo' => ['Estimates' => ['foreignKey' => 'estimate_id']],
'hasOne' => ['Taxes' => ['foreignKey' => 'tax_id']]
]);
}
Estimate Entity
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Estimate extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* #var array
*/
protected $_accessible = [
'*' => false,
'id' => false,
];
}
Looks like you need to implement the check via a beforeSave callback function if you don't trust that setting fields hidden / not editable is not enough.
In the callBack you can check if the relation was already there before before you overwrite them with a wrongly edited value.
Markstory Replied to me on github with a solution credits to him:
https://github.com/cakephp/cakephp/issues/9527
In Model/Table/EstimateItemsTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\RulesChecker;
....
class EstimateItemsTable extends Table
{
....
public function buildRules(RulesChecker $rules){
$rules->addUpdate(function($entity) {
if (!$entity->dirty('estimate_id')) {
return true;
}
return $entity->estimate_id == $entity->getOriginal('estimate_id');
}, 'ownership', ['errorField' => 'estimate_id']);
return $rules;
}
}

yii2 Dimensions to be validated and display the error message in the form

In yii2 project I have my own file structure setup. Anything uploaded will get saved as a file type. I can get the file dimensions using the file uploaded in the temp folder by yii2. Using those dimensions I set my own width and height and compare them. If the height and width is more than what I have declared It has display an error message in the form. Which I am unable to do it.
My Active Form
<div class="company-form">
<?php
$form = ActiveForm::begin([
'action'=>['company/logo', 'id'=>$model->company_id],
'validateOnSubmit' => true,
'options' =>
['enctype' => 'multipart/form-data','class' => 'disable-submit-buttons','id'=> 'companyLogoForm'],
'fieldConfig' => [
'template' => "<div class=\"row\">
<div class=\"col-xs-6 margin-top-8\">{label}</div>\n<div class=\"col-xs-6 text-right\">{hint}</div>
\n<div class=\"col-xs-12 \">{input}</div>
</div>",
],
]); ?>
<?= $form->errorSummary($model, $options = ['header'=>'','class'=>'pull-left']); ?>
<?= $form->field($model, 'company_name')->hiddenInput(['maxlength' => true])->label(false) ?>
<?= $form->field($file, 'file')->fileInput([])->label(Yii::t('app', 'Attach Logo'),['class'=> 'margin-top-8']) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Save') : Yii::t('app', 'Save'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary','data' => ['disabled-text' => 'Please Wait']]) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
My Controller Action
public function actionLogo($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$file = new File;
$file->load(Yii::$app->request->post());
$a = UploadedFile::getInstance($file,'file');
$size = getimagesize($a->tempName);
$maxWidth = 500;
$maxHeight = 500;
if ($size[0] > $maxWidth || $size[1] > $maxHeight)
{
$model->addError('file', $error = 'Error Message');
if($model->hasErrors()){
return ActiveForm::validate($model);
}
}
$file->file = UploadedFile::getInstance($file,'file');
$file->file_name = $file->file->name;
$file->file_user = Yii::$app->user->id;
$file->file_type = 1;
if($file->save()){
$file->file_path = Files::getFilePath($file->file_id);
$validDir = $file->file->createFileDir($file->file_path, $file->file_id);
if($validDir){
$file->file->saveAs($file->file_path, false);
if($file->save()){
$model->company_file = $file->file_id;
$model->save();
return $this->redirect(['index']);
}
}
}
}
}
How do I add error message in the controller and pass that to display on my form on the modal box.
Note: my form is displayed on the modal box.
Thank you!!
You should handle the file processing in your model - or even better, create a specific UploadForm model for this purpose.
In that case you can use File Validation or a custom validator to set errors during model validation.
The built-in yii\validators\FileValidator gives you plenty pf validation rules out of the box.
This is actually pretty well explained in the documentation: Uploading Files
See also the documentation for FileValidator
Example for validating an uploaded image file:
namespace app\models;
use yii\base\Model;
use yii\web\UploadedFile;
class UploadForm extends Model
{
/**
* #var UploadedFile
*/
public $imageFile;
public function rules()
{
return [
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload()
{
if ($this->validate()) {
$this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);
return true;
} else {
return false;
}
}
}
Try this validation rule
['imageFile', 'image', 'minWidth' => 250, 'maxWidth' => 250,'minHeight' => 250, 'maxHeight' => 250, 'extensions' => 'jpg, gif, png', 'maxSize' => 1024 * 1024 * 2],

File Not uploading in yii2

I want to upload an image and save it into my database.
Here, the field name in database is image_path. When, I am trying to upload my image it shows an error: Call to a member function saveAs() on a non-object on line
$customer->file->saveAs('uploads/customer/' . $customer->file->baseName . '.' . $customer->file->extension);
If I print var_dump($customer->file); it returns NULL.
Can anyone help me to resolve this issue.
This is my view:
<?php $form = ActiveForm::begin([
'id' => 'my-profile',
'action' => \Yii::$app->urlManager->createUrl(['/myprofile', 'id_customer' => Yii::$app->user->identity->id_customer]),
'options' => ['enctype'=>'multipart/form-data']
]); ?>
<?= $form->field($customer, 'file')->fileInput() ?>
<?php ActiveForm::end(); ?>
This is my Model:
public $file;
public function rules()
{
return [
['active', 'default', 'value' => self::STATUS_ACTIVE],
['active', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
[['file'],'file'],
[['name', 'image_path'], 'string', 'max' => 200],
];
}
public function attributeLabels()
{
return [
'file' => 'Profile Picture ',
];
}
This is my controller:
public function actionMyprofile(){
$customer->file = UploadedFile::getInstance($customer,'image_path');
$customer->file->saveAs('uploads/customer/' . $customer->file->baseName . '.' . $customer->file->extension);
$customer->file = 'uploads/customer/' . $customer->file->baseName . '.' . $customer->file->extension;
}
View:
<?php $form = ActiveForm::begin([
'id' => 'my-profile',
'action' => \Yii::$app->urlManager->createUrl(['/myprofile', 'id_customer' => Yii::$app->user->identity->id_customer]),
'options' => ['enctype'=>'multipart/form-data']
]); ?>
<?= $form->field($customer, 'image_path')->fileInput() ?>
<?php ActiveForm::end(); ?>
Model:
public function rules()
{
return [
['active', 'default', 'value' => self::STATUS_ACTIVE],
['active', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
[['image_path'],'file'],
[['name', 'image_path'], 'string', 'max' => 200],
];
}
public function attributeLabels()
{
return [
'image_path' => 'Profile Picture ',
];
}
Controller:
public function actionCreate()
{
$model = new YourModel_name();
if ($model->load(Yii::$app->request->post())) {
$model->image_path = UploadedFile::getInstance($model, 'image_path');
$filename = pathinfo($model->image_path , PATHINFO_FILENAME);
$ext = pathinfo($model->image_path , PATHINFO_EXTENSION);
$newFname = $filename.'.'.$ext;
$path=Yii::getAlias('#webroot').'/image/event-picture/';
if(!empty($newFname)){
$model->image_path->saveAs($path.$newFname);
$model->image_path = $newFname;
if($model->save()){
return $this->redirect(['your redirect_path', 'id' => $model->id]);
}
}
}
return $this->render('create', [
'model' => $model,
]);
}

`Skip on empty` not working in Yii2 file upload

I have a provision to upload logo for companies in my application. Uploading and saving on creating profile works fine. But on update, logo goes empty if I am not uploading it again!
Here's my update form
<?php $form = ActiveForm::begin([
'options' => ['enctype'=>'multipart/form-data']
]); ?>
.....
<?= $form->field($model, 'logo')->fileInput() ?>
...
My Controller action
if ($model->load($_POST) ) {
$file = \yii\web\UploadedFile::getInstance($model, 'logo');
if($file){
$model->logo=$file; }
if($model->save()){
if($file)
$file->saveAs(\Yii::$app->basePath . '/web/images/'.$file);
}
return $this->redirect(['profile']);
} else {
return $this->renderPartial('update', [
'model' => $model,
]);
}
My Rules:
public function rules()
{
return [
[['logo'], 'image', 'extensions' => 'jpg,png', 'skipOnEmpty' => true],
[['name'], 'required'],
[['name', 'description'], 'string'],
];
}
Any ideas????
skipOnEmpty does not apply here because in the update action the $model->logo attribute will not be empty, it will be a string with the file name.$file is still an array with only keys, but not values if not uploaded again. So checked the $file->size instead of checking !empty($file). Fixed the issue by modifying the controller code as follows!
$model = $this->findModel($id);
$current_image = $model->featured_image;
if ($model->load(Yii::$app->request->post())) {
$image= UploadedFile::getInstance($model, 'featured_image');
if(!empty($image) && $image->size !== 0) {
//print_R($image);die;
$image->saveAs('uploads/' . $image->baseName . '.' .$image->extension);
$model->featured_image = 'uploads/'.$image->baseName.'.'.$image->extension;
}
else
$model->featured_image = $current_image;
$model->save();
return $this->redirect(['update', 'id' => $model->module_id]);
} else {
return $this->render('add', [
'model' => $model,
]);
}
'skipOnEmpty' => !$this->isNewRecord
For update it can be skipped.

change placeholder text from array

I'm using the yii-user extension and i'm trying the add proper label to the 'placeholder' attribute. really new to Yii so still trying to get the grasp of things.
I've added the attributeLabels() method in the class in the models folder.
class RegistrationForm extends User {
/**
* Declares attribute labels.
*/
public function attributeLabels()
{
return array(
'email'=>'Email Address',
'firstname'=>'First Name',
'lastname' => 'Last Name',
'verifyPassword' = 'Retype Password'
);
}
}
Here is my code in my /views/ folder
$form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(
'id'=>'registration-form',
'type'=>'vertical',
'enableClientValidation'=>true,
'clientOptions'=>array(
'validateOnSubmit'=>true,
),
));
<?php echo $form->textField($model,'email', array('class' => 'input-block-level', 'placeholder' => 'email')); ?>
<?php echo $form->passwordField($model,'password', array('class' => 'input-block-level', 'placeholder' => 'password')); ?>
<?php echo $form->passwordField($model,'verifyPassword', array('class' => 'input-block-level', 'placeholder' => 'verifyPassword')); ?>
<?php
$profileFields=Profile::getFields();
if ($profileFields) {
foreach($profileFields as $field) {
if ($widgetEdit = $field->widgetEdit($profile)) {
//echo $widgetEdit;
} elseif ($field->range) {
echo $form->dropDownList($profile,$field->varname,Profile::range($field->range),array('class' => 'input-block-level'));
} elseif ($field->field_type=="TEXT") {
echo $form->textArea($profile,$field->varname,array('rows'=>6, 'cols'=>50));
} else {
//echo $field->varname;
if ($field->varname == 'firstname')
{
$placeholder = 'First Name';
}
else if ($field->varname == 'lastname')
{
$placeholder = 'Last Name';
}
else
{
$placeholder = $field->varname;
}
echo $form->textField($profile,$field->varname,array('size'=>60,'maxlength'=>(($field->field_size)?$field->field_size:255),'class' => 'input-block-level', 'placeholder' => $placeholder));
}
echo $form->error($profile,$field->varname);
}
}
?>
how would i make attributeLabels() work on my echo $form->textField($profile,$field->varname,array('size'=>60,'maxlength'=>(($field->field_size)?$field->field_size:255),'class' => 'input-block-level', 'placeholder' => $placeholder)); ?
You can get the text label for the specified attribute with getAttributeLabel() like:
$model->getAttributeLabel('verifyPassword');
E.x:
<?php echo $form->passwordField($model,'verifyPassword',
array('class' => 'input-block-level',
'placeholder' => $model->getAttributeLabel('verifyPassword')));
?>
you don't have to edit class RegistrationForm extends User
open protected/modules/user/model/User.php
add add/edit your custom labels in the attributeLabels() method
public function attributeLabels()
{
return array(
'id' => UserModule::t("Id"),
'username'=>UserModule::t("username"),
'password'=>UserModule::t("Password"),
'verifyPassword'=>UserModule::t("Retype Password"),
'firstname'=>UserModule::t("First Name"), //ADDED
'lastname'=>UserModule::t("Last Name"), // ADDED
'email'=>UserModule::t("Email Address"), //EDITED
'verifyCode'=>UserModule::t("Verification Code"),
'activkey' => UserModule::t("Activation Key"),
'createtime' => UserModule::t("Registration Date"),
'create_at' => UserModule::t("Registration Date"),
'lastvisit_at' => UserModule::t("Last Visit"),
'superuser' => UserModule::t("Superuser"),
'status' => UserModule::t("Status"),
);
}
and to get the label to show in your view file. use this
<?php echo $form->passwordField($model,'verifyPassword',
array('class' => 'input-block-level',
'placeholder' => $model->getAttributeLabel('email')));
?>