Modules config file - yii2

I'm my api module's module.php file, i added this
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
// custom initialization code goes here
\Yii::configure($this, require __DIR__ . '/config/main.php');
}
in my controller i have this
public function actionIndex()
{
dd(Yii::$app->getModule('payment')->params['data']);
}
in my modules `main.php i have this
$params = ['data' => [ ... ]];
if (YII_ENV == 'dev') {
if (YII_ENV == 'dev') {
$params = array_merge(
require __DIR__ . '/../../../../common/config/params.php',
require __DIR__ . '/../../../../common/config/params-local.php',
require __DIR__ . '/params.php',
);
} else {
$params = array_merge(
require __DIR__ . '/../../../../common/config/params.php',
require __DIR__ . '/params.php',
);
}
return [
'params' => $params,
];
I'm getting this error Undefined array key "data" Any one know what im missing here, i'm trying to create a module config. Thank you.

There is not a main.php in module (there is in app/config).
in each module there is a Module.php class in modules/your_module scaffolding
If you need a specific $params array for the a module you should declare the params var
and assign your values or in declaration or in init() function
class Module extends \yii\base\Module
{
public $params = [];
.....
......
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
// custom initialization code goes here
$params['data'] = [......];
}

Related

How can I use same code in different functions in Symfony Controller?

In my Controller I am using several functions. In this functions I am using similar code.
So I am wondering if there is a possibility outsource this code to not have to write it repeatedly. If this is possible, what would be the best way to do it?
class PagesController extends AbstractController
{
/**
* #Route("/documents/{slug}", name="documents", methods={"GET","POST"})
*/
public function documents($slug, Request $request)
{
$page = $this->getDoctrine()->getRepository(Pages::class)->findOneBy(['slug'=>$slug]);
$entityManager = $this->getDoctrine()->getManager();
$cmf = $entityManager->getMetadataFactory();
$classes = $cmf->getMetadataFor($relation_name);
$fieldMappings = $classes->fieldMappings;
$associationMappings = $classes->associationMappings;
$fields = (object)array_merge((array)$fieldMappings, (array)$associationMappings);
}
/**
* #Route("/blog/{slug}", name="single", methods={"GET","POST"})
*/
public function blog($slug, Request $request)
{
$page = $this->getDoctrine()->getRepository(Pages::class)->findOneBy(['slug'=>$slug]);
$entityManager = $this->getDoctrine()->getManager();
$cmf = $entityManager->getMetadataFactory();
$classes = $cmf->getMetadataFor($relation_name);
$fieldMappings = $classes->fieldMappings;
$associationMappings = $classes->associationMappings;
$fields = (object)array_merge((array)$fieldMappings, (array)$associationMappings);
}
/**
* #Route("/contact/{slug}", name="contact", methods={"POST", "GET"})
*/
public function contact($slug, Request $request)
{
$page = $this->getDoctrine()->getRepository(Pages::class)->findOneBy(['slug'=>$slug]);
$entityManager = $this->getDoctrine()->getManager();
$cmf = $entityManager->getMetadataFactory();
$classes = $cmf->getMetadataFor($relation_name);
$fieldMappings = $classes->fieldMappings;
$associationMappings = $classes->associationMappings;
$fields = (object)array_merge((array)$fieldMappings, (array)$associationMappings);
}
}
You can use private method and call it, but in your case you could use Page typehint right in the parameter:
/**
* #Route("/contact/{slug}", name="contact", methods={"POST", "GET"})
*/
public function contact(Page $slug, Request $request)
The keyword here is services. Move your business logic to a other classes and auto-inject it in your controller using autowiring. This is a Symfony Best Practice:
Symfony follows the philosophy of "thin controllers and fat models".
This means that controllers should hold just the thin layer of
glue-code needed to coordinate the different parts of the application.
You should read about these best practices!
You can inject services in your controller class and in a specific action:
class PagesController extends AbstractController
{
public function __construct(Rot13Transformer $transformer)
{
$this->transformer = $transformer;
}
/**
* #Route("/documents/{slug}", name="documents", methods={"GET","POST"})
*/
public function documents($slug, Request $request, PagesRepository $repo)
{
$page = $repo->findOneBy(['slug'=>$slug]);
$foo = $repo->doSomethingDifferentWithEntities($page)
$bar = $this->transformer->transform($foo);
}
}
#Jarla Additionally to #Stephan Vierkant answer you can use #ParamConverter annotation
In your case, it will be:
/**
* #Route("/documents/{slug}", name="documents", methods={"GET","POST"})
* #ParamConverter("page", options={"mapping": {"slug": "slug"}})
*/
public function documents(Page $page, Request $request)
{
$foo = $repo->doSomethingDifferentWithEntities($page)
$bar = $this->transformer->transform($foo);
}

Invalid argument in foreach loop using a model

Hey I am making a code in which I m using foreach loop but its giving me error of invalid argument.
Loop of my code is
public function handle($request, Closure $next)
{
foreach(Auth::user()->User as $role){
if($role->role == 'doctor')
{
return $next($request);
}
}
return redirect('');
}
And the model is
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
Please tell me what I am doing wrong.
You can't do a foreach on Auth:user(). If you want to validate the role of a user, you can get the user object that is doing the request and validate the role, for example:
public function handle($request, Closure $next)
{
if($request->user()->rol == 'admin'){
return $next($request);
}
return redirect('/something');
}

Upload the file to a folder and saving the name to the database in yii2

Good afternoon!
There is a question about the file upload to yii2. There are two folders in it that will store the original and thumbnail image. At me files are loaded but here the name of a file does not load in a database
Model
namespace app\models;
use yii\base\Model;
use yii\db\ActiveRecord;
use yii\web\UploadedFile;
use yii\imagine\Image;
use Imagine\Image\Box;
/**
* This is the model class for table "images".
*
* #property integer $id
* #property string $original_image
* #property string $prev_image
*/
class Images extends ActiveRecord
{
public $imageFile;
public $file_name;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'images';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['prev_image'], 'string', 'max' => 255],
[['original_image'], 'string'],
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxSize' => 1024 * 1024 * 7],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'original_image' => 'Original Image',
'prev_image' => 'Prev Image',
];
}
public function upload()
{
$temp=substr(md5(microtime() . rand(0, 9999)), 0, 20);
if ($this->validate()) {
$this->imageFile->saveAs('uploads/original/'.$temp.$this->imageFile->baseName . '.' . $this->imageFile->extension);
$imagine = Image::getImagine();
$image = $imagine->open('uploads/original/' . $temp.$this->imageFile);
$image->resize(new Box(250, 150))->save('uploads/prev/' . $temp.$this
->imageFile->baseName . '.' . $this->imageFile->extension, ['quality' => 70]);
$this->file_name=$temp.$this->imageFile->baseName . '.' . $this->imageFile->extension;
return true;
} else {
return false;
}
}
}
Controller
namespace app\controllers;
use app\models\Images;
use Yii;
use yii\web\UploadedFile;
class ImageController extends \yii\web\Controller
{
public function actionUpload()
{
$model = new Images();
if ($model->load(Yii::$app->request->post())) {
$model->imageFile = UploadedFile::getInstance($model, 'imageFile');
$model->prev_image=$model->file_name;
$model->original_image=$model->file_name;
$model->save();
if ($model->upload()) {
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}
A question how to save a file name in database? Thank you in advance
It's because save happens before uplaod action, but you only define file_name in upload function. Save is what saves it in to the database.
Controller should look like this:
namespace app\controllers;
use app\models\Images;
use Yii;
use yii\web\UploadedFile;
class ImageController extends \yii\web\Controller
{
public function actionUpload(){
$model = new Images();
if ($model->load(Yii::$app->request->post())) {
$uploadedFile = UploadedFile::getInstance($model, 'imageFile');
$model->imageFile = $uploadedFile;
$model->prev_image = $uploadedFile->name
$model->original_image = $uploadedFile->name
$model->save();
if ($model->upload()) {
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}

Move file from one directory to other directory in yii2

I have uploaded file on my shared server now I want to move file using yii2 libraries how can I move this file.
Simple use this:
http://php.net/manual/en/function.rename.php
or in uploadAction you can use saveAs method when you upload file like this:
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]);
}
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;
}
}
}
manual:
http://www.yiiframework.com/doc-2.0/guide-input-file-upload.html

phpunit mock method called in constructor

I test a config class, which is parsing a config file and allows me to get the various settings for an app.
My goal is to mock the parse() method of the Config class, which is called in the constructor and to set what this method is returning in the constructor.
This way, it prevents file_get_contents() from being called (in the parse() method) and enables me to have a Config class with the config property already set to contain an array of properties.
But I haven't succeeded doing that.
Here is the code:
The config class:
<?php namespace Example;
use Symfony\Component\Yaml\Parser;
class Config
{
private $parser;
private $config;
public function __construct(Parser $parser, $filePath)
{
$this->parser = $parser;
$this->config = $this->parse($filePath);
}
public function parse($filePath)
{
$fileAsString = file_get_contents($filePath);
if (false === $fileAsString) {
throw new \Exception('Cannot get config file.');
}
return $this->parser->parse($fileAsString);
}
public function get($path = null)
{
if ($path) {
$config = $this->config;
$path = explode('/', $path);
foreach ($path as $bit) {
if (isset($config[$bit])) {
$config = $config[$bit];
}
}
return $config;
}
return false;
}
}
The test:
<?php namespace Example;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
private function getConfigTestMock($configAsArray)
{
$parser = $this->getMockBuilder('\Symfony\Component\Yaml\Parser')
->getMock();
$configMock = $this->getMockBuilder('Example\Config')
->setConstructorArgs([$parser, $configAsArray])
->setMethods(['parse', 'get'])
->getMock();
$configMock->expects($this->once())
->method('parse')
->willReturn($configAsArray);
return $configMock;
}
/**
* #test
*/
public function get_returns_false_if_no_path_given()
{
$configMock = $this->getConfigTestMock(['param1' => 'value1']);
// Testing further...
}
}
I suggest you to make a functional test mocking the interaction with the file system, without do partial mocking of the tested class.
I recently discover the vfsStream library used in a great article of William Durand about Symfony2 and DDD.
So you can install this library in your composer.json (I tested the solution with the 1.4 version) and try this example test class:
<?php
namespace Acme\DemoBundle\Tests;
use Acme\DemoBundle\Example\Config;
use org\bovigo\vfs\vfsStream;
use Symfony\Component\Yaml\Parser;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
/**
* #test
*/
public function valid_content()
{
$content = "param1: value1";
$root = vfsStream::setup();
$file = vfsStream::newFile('example.txt')
->withContent($content)
->at($root);
$filepath = $file->url();
$parser = new Parser();
$config = new Config($parser, $filepath);
$this->assertEquals("value1", $config->get("param1"));
}
}
Hope this help
For test the Config class you need to mock only the Parser and use the real Config class. As Example:
<?php
namespace Acme\DemoBundle\Tests;
use Acme\DemoBundle\Example\Config;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
private function getConfigTestMock($configAsArray)
{
$parser = $this->getMockBuilder('\Symfony\Component\Yaml\Parser')
->getMock();
$parser->expects($this->once())
->method('parse')
->willReturn($configAsArray);
$configMock = new Config($parser,"fakePath");
return $configMock;
}
/**
* #test
*/
public function get_returns_false_if_no_path_given()
{
$configMock = $this->getConfigTestMock(['param1' => 'value1']);
$this->assertEquals("value1",$configMock->get("param1"));
// Testing further...
}
}
Hope this help