How to upload files in web folder in yii2 advanced template? - yii2

I try to upload files in backend and each time i uploaded a file it was successfully uploaded and successfully saved in the DB but it wasn't save to the directory i specified so my application can't find the file, and i already gave 777 permission to the uploads folder in web directory. below is my codes
Model to handle and save the file upload...
How to upload files in root folder in yii2 advanced template?
Model responsible for the upload
<?php
namespace backend\models;
use yii\base\Model;
use yii\web\UploadedFile;
use yii\Validators\FileValidator;
class UploadForm extends Model {
public $img;
public $randomCharacter;
public function rules(){
return[
[['img'], 'file', 'skipOnEmpty' => false, 'extensions'=> 'png, jpg,jpeg'],
];
}
public function upload(){
$path = '/uploads/';
$randomString = '';
$length = 10;
$character = "QWERTYUIOPLKJHGFDSAZXCVBNMlkjhgfdsaqwertpoiuyzxcvbnm1098723456";
$randomString = substr(str_shuffle($character),0,$length);
$this->randomCharacter = $randomString;
if ($this->validate()){
$this->img->saveAs($path .$randomString .'.'.$this->img->extension);
return true;
}else{
return false;
}
}
}
The product model reponsible for save info into database
<?php
namespace backend\models;
use Yii;
use yii\base\Model;
use yii\web\UploadedFile;
use yii\Validators\FileValidator;
class Products extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'products';
}
public function rules()
{
return [
[['name'], 'string', 'max' => 100],
[['descr', 'img', 'reviews'], 'string', 'max' => 255],
// [['file'], 'file', 'skipOnEmpty' => false, 'extensions'=> 'png, jpg,jpeg']
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'img' => 'Img',
];
}
}
my controller and the action
public function actionCreate()
{
$time = time();
$model = new Products();
$upload = new UploadForm();
if ($model->load(Yii::$app->request->post()) ) {
//get instance of the uploaded file
$model->img = UploadedFile::getInstance($model, 'img');
//define the file path
$upload->upload();
//save the path in the db
$model->img = 'uploads/' .$upload->randomCharacter .'.'.$model->img->extension;
$model->addtime = $time;
$model->save();
return $this->redirect(['view', 'product_id' => $model->product_id]);
}else {
return $this->render('create', [
'model' => $model,
]);
}
}
and my view file has been modified too thanks for any help

$path = '/uploads/';
That means you are uploading files to /uploads, the top level folder in the system.
Run cd /uploads in your terminal and check, most likely files were uploaded there.
To fix that, you need to specify correct full path. The recommended way to do it is using aliases.
In case of advanced application, you already have #backend alias, so you can use it:
$this->img->saveAs(\Yii::getAlias("#backend/web/uploads/{$randomString}.{$this->img->extension}");
Or create your own alias in common/config/bootstrap.php:
Yii::setAlias('uploads', dirname(dirname(__DIR__)) . '/backend/web/uploads');
And then use it like this:
Yii::getAlias('#uploads');
Don't forget to include use Yii if you are not in root namespace or use \Yii;
If you want your images to be accessible on frontend, you can create symlink between frontend and backend images folders.

Related

Yii2 creating a custom function in the model using Gii model generator

I am working on Yii2 using Gii to generate models. What I am trying to do is to customize my models such that all of them will have the following function
public static function getFoobarList()
{
$models = Foobar::find()->all();
return ArrayHelper::map($models, 'id', 'foobar');
}
Where Foobar is the name of individual models.
Thank you in advance.
You can create a custom template for your models which gii can use to generate your class.
Something like the following, added to the top of a copy of the file /vendor/yiisoft/yii2-gii/generators/model/default/model.php and the new file stored in, for example, #app/myTemplates/model/default
/**
* your doc string
*/
public static function get<?php echo $className; ?>List()
{
$models = static::find()->all();
return ArrayHelper::map($models, 'id', static::tableName());
}
will add the method you're looking for to any model created with the new template.
In your config something like
// config/web.php for basic app
// ...
if (YII_ENV_DEV) {
$config['modules']['gii'] = [
'class' => 'yii\gii\Module',
'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'],
'generators' => [ //here
'model' => [ // generator name
'class' => 'yii\gii\generators\model\Generator', // generator class
'templates' => [ //setting for out templates
'myModel' => '#app/myTemplates/model/default', // template name => path to template
]
]
],
];
}
will allow you to select your custom template when using gii, from the 'Code Template' menu.
Since you want this in all the models, another solution would be to add this function in ActiveRecord Model from which all generated models extend. You just need to change the function a bit to perform the required functionality.
Just add this to your ActiveRecord class:
public static function getModelList()
{
$models = static::find()->all();
return ArrayHelper::map($models, 'id', static::tableName());
}
To use this for any model, example Foobar all you'll need to do is:
Foobar::getModelList();

Yii2 kartik file FileInput Multiple

So I am working with Yii2 and am fairly new to it. I am using Kartik File upload and have attempted to convert the code for multiple files. but it only saves the first file.
I have removed the validation as this was also failing but will add back in once I know all else is working.
Model:
/**
* Process upload of image
*
* #return mixed the uploaded image instance
*/
public function uploadImage() {
// get the uploaded file instance. for multiple file uploads
// the following data will return an array (you may need to use
// getInstances method)
$image = UploadedFile::getInstances($this, 'image');
foreach ($image as $images) {
// if no image was uploaded abort the upload
if (empty($images)) {
return false;
}
// store the source file name
$this->image_src_filename = $images->name;
$extvar = (explode(".", $images->name));
$ext = end($extvar);
// generate a unique file name
$this->image_web_filename = Yii::$app->security->generateRandomString().".{$ext}";
// the uploaded image instance
return $images;
} }
Controller:
/**
* Creates a new PhotoPhotos model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new PhotoPhotos();
if ($model->load(Yii::$app->request->post())) {
// process uploaded image file instance
$images = $model->uploadImage();
if ($model->save(false)) {
// upload only if valid uploaded file instance found
if ($images !== false) {
$path = $model->getImageFile();
$images->saveAs($path);
}
return $this->redirect(['view', 'id' => $model->ID]);
} else {
//error in saving
}
}
return $this->render('create', [
'model' => $model,
]);
}
View:
//uncomment for multiple file upload
echo $form->field($model, 'image[]')->widget(FileInput::classname(), [
'options'=>['accept'=>'image/*', 'multiple'=>true],
]);
I see one problem which is that you reversed $images and $image in
foreach ($image as $images)
which should be
foreach ($images as $image)
Cheers

Yii2 POST image to model in API without Yii2 Naming convention

I'm creating an endpoint for a mobile application to send a image to the server. I'm posting the image with the POSTMAN extension for chrome. The image is in the $_FILES variable, and named image. How can I load this image into a model, or the UploadedFile class? The $model->load(Yii::$app->request->post()) line does not correctly load the file, as it is not in Yii2's naming convention for forms.
It's currently returning:
{
"success": false,
"message": "Required parameter 'image' is not set."
}
Code
models\Image.php
<?php
namespace api\modules\v1\models;
use yii\base\Model;
use yii\web\UploadedFile;
class Image extends Model
{
/**
* #var UploadedFile
*/
public $image;
public function rules()
{
return [
[['image'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload()
{
$path = dirname(dirname(__FILE__)) . '/temp/';
if ($this->validate()) {
$this->image->saveAs($path . $this->image->baseName . '.' . $this->image->extension);
return true;
} else {
die(var_dump($this->errors));
return false;
}
}
}
controllers\DefaultController.php
<?php
namespace api\modules\v1\controllers;
use api\modules\v1\models\Image;
use yii\web\Controller;
use yii\web\UploadedFile;
use Yii;
class DefaultController extends Controller
{
public $enableCsrfValidation = false;
public function actionIndex()
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$model = new Image();
if (Yii::$app->request->isPost) {
if($model->load(Yii::$app->request->post()))
{
$model->image = UploadedFile::getInstance($model, 'image');
if ($model->upload()) {
// file is uploaded successfully
return ['success' => true, 'message' => 'File saved.'];
}
else return ['success' => false, 'message' => 'Could not save file.'];
}
else return ['success' => false, 'message' => 'Required parameter \'image\' is not set.'];
}
else return ['success' => false, 'message' => 'Not a POST request.'];
}
}
Postman
Your problem seems to be the name you are using to send the image file. Usually Yii2 uses names for form attributes like "ModelName[attributeName]" and you are sending your image file with the name "image"
There are 2 ways of fixing this:
Change the name you use to send your image file to follow the same naming conveniton. However you don't seem to want that.
Use getInstanceByName('image') method instead of getInstance($model, 'image')
The problem come here
When you send files via api they are not sent asynchronously. If you check
echo '<pre>';
print_r($_FILES); //returns nothing
print_r($_POST["image"]); //returns something
echo '</pre>';
die;
One reason is that your controller extendsyii\web\controller which is not used by rest apis, extend yii\rest\controller
The other way to go about this is by using javascript formData when sending the post request
This is a way i handled a previous ajax post of an image probably itll give you a guideline
The form
<?php $form = ActiveForm::begin(['options' => ['enctype' =>
'multipart/form-data','id'=>'slider_form']]); ?> //dont forget enctype
<?= $form->field($model, 'file')->fileInput() ?>
Then on the ajax post
var formData = new FormData($('form#slider_form')[0].files);
$.post(
href, //serialize Yii2 form
{other atributes ,formData:formData}
)
Then on the controller simply access via
$model->file =$_FILES["TblSlider"]; //here this depends on your form attributes check with var_dump($_FILES)
$file_tmp = $_FILES["TblSlider"]["tmp_name"]["file"];
$file_ext = pathinfo($_FILES['TblSlider']['name']["file"], PATHINFO_EXTENSION);
if(!empty($model->file)){
$filename = strtotime(date("Y-m-d h:m:s")).".".$file_ext;
move_uploaded_file($file_tmp, "../uploads/siteimages/slider/".$filename);
///move_uploaded_file($file_tmp, Yii::getAlias("#uploads/siteimages/slider/").$filename);
$model->image = $filename;
}
I hope this helps

Import Excel in Yii2

I have yii2 application using advanced template and database mySql, i already make function for import an excel file to one of the table,
I made the function in a controller named student that contains CRUD of students data.this is my code
public function actionImportExcel()
{
$inputFile = 'uploads/siswa_file.xlsx';
try{
$inputFileType = \PHPExcel_IOFactory::identify($inputFile);
$objReader = \PHPExcel_IOFactory::createReader($inputFileType);
$objPHPExcel = $objReader->load($inputFile);
} catch (Exception $e) {
die('Error');
}
$sheet = $objPHPExcel->getSheet(0);
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
for($row=1; $row <= $highestRow; $row++)
{
$rowData = $sheet->rangeToArray('A'.$row.':'.$highestColumn.$row,NULL,TRUE,FALSE);
if($row==1)
{
continue;
}
$siswa = new Siswa();
$siswa->nis = $rowData[0][0];
$siswa->nama_siswa = $rowData[0][1];
$siswa->jenis_kelamin = $rowData[0][2];
$siswa->ttl = $rowData[0][3];
$siswa->alamat = $rowData[0][4];
$siswa->telp = $rowData[0][5];
$siswa->agama = $rowData[0][6];
$siswa->nama_ortu = $rowData[0][7];
$siswa->telp_ortu = $rowData[0][8];
$siswa->pekerjaan_ortu = $rowData[0][9];
$siswa->tahun_masuk = $rowData[0][10];
$siswa->kelas = $rowData[0][11];
$siswa->save();
print_r($siswa->getErrors());
}
die('okay');
}
but i don't know how to make a button in a view to make this function work. i mean i want to make a button that when the user click the button and browse their excel file they can import that file and the data inside the excel can import to database
First you should upload the file
and then processing with your function
there are several parts of code you must produce ..
eg a view for the user to upload the file
View: #app/views/site/upload.php
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
<?= $form->errorSummary($model); ?>
<?= $form->field($model, 'imageFile')->fileInput() ?>
<button>Submit</button>
<?php ActiveForm::end() ?>
Controller: #app/controllers/SiteController.php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\UploadForm;
use yii\web\UploadedFile;
class SiteController extends Controller
{
public function actionUpload()
{
$model = new UploadForm();
if (Yii::$app->request->isPost) {
$model->imageFile = UploadedFile::getInstance($model, 'imageFile');
if ($model->upload()) {
// file is uploaded successfully
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}
Model: #app/models/UploadForm.php
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;
}
}
}
the code is from this doc

How to access configs from autoloaded config files in a layout / view script in Zend Framework 2?

I would like / have to manage some settings in ZF1 style and provide the view with the infomation about the current environment.
/config/application.config.php
return array(
...
'module_listener_options' => array(
...
'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php')
)
);
/config/autoload/env.local.php
return array(
// allowed values: development, staging, live
'environment' => 'development'
);
In a common view script I can do it over the controller, since the controllers have access to the Service Manager and so to all configs I need:
class MyController extends AbstractActionController {
public function myAction() {
return new ViewModel(array(
'currentEnvironment' => $this->getServiceLocator()->get('Config')['environment'],
));
}
}
Is it also possible to get the configs in a common view directly?
How can I access the configs in a layout view script (/module/Application/view/layout/layout.phtml)?
(My implementation/interpretation of) Crisp's suggestion:
Config view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class Config extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
public function __invoke() {
$config = $this->serviceManager->getServiceLocator()->get('Config');
return $config;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'config' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\Config($serviceManager);
return $helper;
},
)
);
}
Layout view script
// do whatever you want with $this->config()['environment'], e.g.
<?php
if ($this->config()['environment'] == 'live') {
echo $this->partial('partials/partial-foo.phtml');;
}
?>
My implementation/interpretation of Sam's solution hint for the concret goal (to permit displaying the web analytics JS within the dev/staging environment):
DisplayAnalytics view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class DisplayAnalytics extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
public function __invoke() {
if ($this->serviceManager->getServiceLocator()->get('Config')['environment'] == 'development') {
$return = $this->view->render('partials/partial-bar.phtml');
}
return $return;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'displayAnalytics' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\DisplayAnalytics($serviceManager);
return $helper;
}
)
);
}
Layout view script
<?php echo $this->displayAnalytics(); ?>
So this solution becomes more flexible:
/config/application.config.php
return array(
...
'module_listener_options' => array(
...
'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php')
)
);
/config/autoload/whatever.local.php and /config/autoload/whatever.global.php
return array(
// allowed values: development, staging, live
'environment' => 'development'
);
ContentForEnvironment view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class ContentForEnvironment extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
/**
* Returns rendered partial $partial,
* IF the current environment IS IN $whiteList AND NOT IN $blackList,
* ELSE NULL.
* Usage examples:
* Partial for every environment (equivalent to echo $this->view->render($partial)):
* echo $this->contentForEnvironment('path/to/partial.phtml');
* Partial for 'live' environment only:
* echo $this->contentForEnvironment('path/to/partial.phtml', ['live']);
* Partial for every environment except 'development':
* echo $this->contentForEnvironment('path/to/partial.phtml', [], ['development', 'staging']);
* #param string $partial
* #param array $whiteList
* #param array $blackList optional
* #return string rendered partial $partial or NULL.
*/
public function __invoke($partial, array $whiteList, array $blackList = []) {
$currentEnvironment = $this->serviceManager->getServiceLocator()->get('Config')['environment'];
$content = null;
if (!empty($whiteList)) {
$content = in_array($currentEnvironment, $whiteList) && !in_array($currentEnvironment, $blackList)
? $this->view->render($partial)
: null
;
} else {
$content = !in_array($currentEnvironment, $blackList)
? $this->view->render($partial)
: null
;
}
return $content;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'contentForEnvironment' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\ContentForEnvironment($serviceManager);
return $helper;
}
)
);
}
Layout view script
<?php echo $this->contentForEnvironment('path/to/partial.phtml', [], ['development', 'staging']); ?>