Yii use of Component and behaviour - php

How Could I make generalised function which are used by every Model and Controllers Using Component as well behaviour??

//your behavior
class TestBehavior extends CActiveRecordBehavior
{
public $a = '';
public $b = '';
public function getA(){
return $this->a;
}
public function getB(){
return $this->b;
}
...
}
//in your model
public function behaviors(){
return array(
'TestBehavior' => array(
'class' => 'TestBehavior',
'a' => 'value a ',
'b' => 'value b',
),
//one more behavior
);
}
...
// use model behavior:
echo $model->TestBehavior->getA();
// in controller you can use attachBehavior() it will be like:
$this->attachBehavior('TestBehavior', array(
'class' => 'TestBehavior',
));
Hope this help you.

Related

PHP: Using Classes to store Non-dynamic data instead of database

In a BI project we have multiple reporter functionalities. So, we have defined some classes to implement this feature. This classes needs many attributes to chain and build complex queries to generate reports. Any other classes should set specific values for these attributes, to get reports from this class. Values of these attributes are Non-Dynamic. I don't use the database to store them.
Below codes are the current model i am using:
Report generator (Main class):
class Report
{
private $indicator;
private $ratio;
private $divider;
private $criteria;
private $plantation;
private $reporter;
public function reporter($reporter)
{
$this->reporter = (new Reporters())->get($reporter);
return $this;
}
public function plantation($plantationId)
{
$this->plantation = $plantationId;
return $this;
}
public function ratio($ratio)
{
$this->ratio = (new Ratios())->get($ratio);
return $this;
}
public function divider($divider)
{
$this->divider = (new Dividers())->get($divider);
return $this;
}
public function criteria($criteria)
{
$this->criteria = $criteria;
return $this;
}
public function get()
{
return $this->mocker();
}
}
Dividers Class:
class Dividers
{
public $dividers = [
'sum' => [
'name' => 'مجموع',
'alias' => 'sum',
],
'plantations' => [
'name' => 'مجموعه انتخابی',
'alias' => 'plantations',
'model' => Plantation::class
],
'operation_types' => [
'name' => 'نوع عملیات',
'alias' => 'operation_type',
'model' => OperationType::class
],
'planting_years' => [
'name' => 'سال زراعی',
'alias' => 'planting_years',
'model' => Planting_year::class
],
'crops' => [
'name' => 'انواع گیاهان',
'alias' => 'crops',
'model' => Crop::class
],
];
public function get($divider)
{
if(!array_key_exists($divider, $this->dividers)){
return false;
}
return $this->dividers[$divider];
}
}
Ratio Class:
class Ratios
{
public $ratios = [
'SUM' => 'انباشته',
'KILOGRAM' => 'کیلوگرم',
'HECTARE' => 'هکتار',
'RIALPERKILO' => 'ریال به کیلوگرم',
'MILIONRIALPERTON' => 'میلیون ریال بر تن',
];
public function get($ratio)
{
if(!array_key_exists($ratio, $this->ratios)){
return false;
}
return $this->ratios[$ratio];
}
}
So for using report generator i will use this method:
$report = (new Report())
->plantation(352)
->divider('sum')
->reporter('NetProfit', ['operation_type'=> 364])
->criteria([['criteriaType'=> 'human_resources', 'value'=> '256'],['criteriaType'=> 'human_resources', 'value'=> '326']])
->ratio('sum')
->indicator(324, 523, 632)
->get();
My question is: what is the best pattern to store this data objects to reduce human mistakes?
This is more of an opinion based answer, so I'll suggest what I do when I am using static values.
Declare class members as static and protected for variables.
Like in your question class Ratios { } is a static class
class Ratios{
protected static $ratios = [...];
public static function get($ratio)
{
if(!array_key_exists($ratio, self::$ratios)){
return false;
}
return self::$ratios[$ratio];
}
}
//Access the Ratios class with.
$val = Ratios::get($ratio);
This ensures that
the values won't change throughout the lifecycle of your request
Adds a layer of security.
Also maintains the source, i.e. no change will occur if you don't change the code.
Doesn't create a new Instance(new Ratios()) for getting static values and you have that memory edge.
Do the same with the class Dividers { }.
I don't know if this is the best practice, but i would make a separate directory called "constants" or "config" or something that seems intuitive to you, and add there files named like Class_Property.php that return the value of that property
For example, in you Ratios class:
class Ratios
{
public $ratios = require('config/Ratios_ratios.php');
public function get($ratio)
{
if(!array_key_exists($ratio, $this->ratios)){
return false;
}
return $this->ratios[$ratio];
}
}
And in config/Ratios_ratios.php:
<?php
return [
'SUM' => 'انباشته',
'KILOGRAM' => 'کیلوگرم',
'HECTARE' => 'هکتار',
'RIALPERKILO' => 'ریال به کیلوگرم',
'MILIONRIALPERTON' => 'میلیون ریال بر تن',
];
?>
Depending on how critical that data is, opt for require/require_once/include. This is done mainly to keep your class more skinny, separating the constants

Is it possible to print all called classes and functions in php?

If I have this example :
class A {
public function test(){
echo 'a';
}
}
class B {
public function test(){
echo 'b;
}
}
class C {
public function test(){
(new A())->test();
}
}
(new A())->test();
(new B())->test();
(new C())->test();
I want to get array with all called functions and classes, something like:
[
'A' => [
'function' => 'test',
'count' => 2,
'miliseconds' => 20,
'miliseconds_each' => [5, 15],
],
'B' => [
'function' => 'test',
'count' => 1,
'miliseconds' => 25,
'miliseconds_each' => [25],
],
'C' => [
'function' => 'test',
'count' => 1,
'miliseconds' => 30,
'miliseconds_each' => [30],
]
]
Notice: I want solution for whole framework not just bad workaround for this small example.
I would do like this, Make a base class and extends to update your analysis.
<?php
class Base{
static $data;
public static function analysis(){
return this::data;
}
function update($function){
if(isset(Base::$data[$function])){
Base::$data[$function] = ['count'=>Base::$data[$function]['count']+1,'function'=>'test'];
}else{
Base::$data[$function] = ['count'=>1,'function'=>'test'];
}
}
}
class A extends Base{
public function test(){
$this->update('a');
echo 'a';
}
}
class B extends Base{
public function test(){
$this->update('b');
echo 'b';
}
}
class C extends Base{
public function test(){
(new A())->test();
}
}
(new A())->test();
(new B())->test();
(new C())->test();
print_r(Base::$data);
?>
I know you can update code to get miliseconds and miliseconds_each
Live demo : https://eval.in/879998

Fatal error: Call to a member function get() on null in Zend Framework 2

I am getting null when using $sm=$this->getServiceLocator() as a result $sm->get("XXXXXXXXXXX") throwing a Fatal error: Call to a member function get() on null.
What i am doing is that, while receiving user data in controller i am calling another controller validatorController inside my requested controller which is signupController and in validatorController i am using $sm=$this->getServiceLocator() which gives the above error
Here is my work
Error comes when i use $check=$this->_getUserTable()->isUnique($email); in ValidatorController.php but not in SignupController.php
Module.php
<?php
namespace User;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\ResultSet\ResultSet;
use User\Controller\ValidatorController;
use User\Model\User;
use User\Model\UserTable;
class Module {
public function getConfig() {
return include __DIR__."/config/module.config.php";
}
public function getAutoloaderConfig() {
return array(
"Zend\loader\StandardAutoloader"=>array(
"namespaces"=>array(
__NAMESPACE__=>__DIR__."/src/".__NAMESPACE__
)
)
);
}
public function getServiceConfig() {
return array(
"factories"=>array(
'User\ValidatorController' => function ($sm) {
$validatorController = new ValidatorController();
return $validatorController;
},
"User\Model\UserTable"=>function($sm) {
$tableGateway=$sm->get("UserTableGateway");
$table=new UserTable($tableGateway);
return $table;
},
"UserTableGateway"=>function($sm) {
$dbAdapter=$sm->get("Zend\Db\Adapter\Adapter");
$resultSetPrototype=new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new User());
return new TableGateway("users",$dbAdapter,null,$resultSetPrototype);
}
)
);
}
}
module.config.php
<?php
return array(
"controllers"=>array(
"invokables"=>array(
"User\Controller\User"=>"User\Controller\UserController",
'User\Controller\Signup' => 'User\Controller\SignupController',
'User\Controller\Validator' => 'User\Controller\ValidatorController'
)
),
// The following section is new and should be added to your file
"router"=>array(
"routes"=>array(
"user"=>array(
"type"=>"segment",
"options"=>array(
"route" => "/user[/:action][/:id]",
"constraints" => array(
"id" => "[0-9]+",
),
"defaults"=>array(
"controller"=>"User\Controller\User"
)
)
),
'signup' => array(
'type' => 'segment',
'options' => array(
'route' => '/signup',
'defaults' => array(
'controller' => 'User\Controller\Signup',
)
)
),
)
),
'view_manager' => array(//Add this config
'strategies' => array(
'ViewJsonStrategy',
),
),
);
SignupController.php
<?php
namespace User\Controller;
use Zend\Mvc\Controller\AbstractRestfulController;
use Zend\View\Model\JsonModel;
class SignupController extends AbstractRestfulController{
private $_userTable;
public function create($data) {
/*
* The above error is not coming here
* $check=$this->_getUserTable()->isUnique($data['email']);
*
* But inside the below controller
*/
// Calling a validatorContoller
$validator=$this->getServiceLocator()->get('User\ValidatorController');
$response=$validator->validateEmail($data['email']);
return new JsonModel($response);
}
public function _getUserTable() {
if(!$this->_userTable) {
$sm=$this->getServiceLocator();
$this->_userTable=$sm->get("User\Model\UserTable");
}
return $this->_userTable;
}
}
ValidatorController.php
<?php
namespace User\Controller;
use Zend\Mvc\Controller\AbstractRestfulController;
use Zend\Validator\EmailAddress;
class ValidatorController extends AbstractRestfulController {
private $_userTable;
public function validateEmail($email) {
$validator = new EmailAddress();
if($validator->isValid($email)) {
// check if it is a unique entry in user table
// ***(THE SOURCE OF ERROR IS HERE)***
$check=$this->_getUserTable()->isUnique($email);
return $check;
}
}
public function _getUserTable() {
if(!$this->_userTable) {
$sm=$this->getServiceLocator();
$this->_userTable=$sm->get("User\Model\UserTable");
}
return $this->_userTable;
}
}
NOTE
Error comes when i use $check=$this->_getUserTable()->isUnique($email); in ValidatorController.php but not in SignupController.php
Thankyou
getServiceLocator() is deprecated in ZendFramework 2. You must inject _userTable in your ValidatorController from your Module.php like this :
class Module {
...
public function getServiceConfig() {
return array(
"factories"=>array(
'User\ValidatorController' => function ($sm) {
$userTable = $sm->get("User\Model\UserTable");
$validatorController = new ValidatorController();
$validatorController->setUserTable($userTable);
return $validatorController;
},
...
}
Then add a setUserTable() method in your ValidController and modify the getUserTable() method :
class ValidController {
public function setUserTable($suerTable) {
$this->_suerTable = $userTable
}
public function _getUserTable() {
return $this->_userTable;
}
}

PHP magic method __invoke, empty instance variables

I'm facing a problem when calling __invoke() on an object. Is __invoke() method agnostic to instance variables? I need to call __invoke() directly on my templates due to some ZF2 injection to call $this->getView()->render(...) (otherwise getView() returns null) and I would like to have instance variables setted there. Any workaround?
See my code:
namespace Person\Person\View\Helper;
use Zend\View\Helper\AbstractHelper;
class PersonShowWidget extends AbstractHelper
{
protected $model = null;
public function __construct(array $options = null)
{
$this->parseOptions($options);
}
public function __invoke()
{
var_dump($this->model); //returns null
return $this->getView()->render('person/show/show_widget', array(
'title' => 'Cliente',
'model' => $this->model,
)
);
}
public function setOptions(array $options = null)
{
$this->parseOptions($options);
}
protected function parseOptions(array $options = null)
{
if (!is_null($options) && is_array($options)) {
if (isset($options['model'])) {
$model = $options['model'];
if (isset($model['id'])) {
$this->model['id'] = $model['id'];
} else {
throw new \Exception;
}
if (isset($model['form'])) {
$this->model['form'] = $model['form'];
} else {
throw new \Exception;
}
}
}
var_dump($this->model); //returns valid data
}
}
I do have called the constructor with some options or the setOptions method before calling __invoke().
Thanks,
You have to initialize the view helper with a factory. In this way you can make sure the constructor is called before the __invoke method is called. And no..the __invoke() method is not agnostic to instance variables.
In the Module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'personShowWidget' => function ($helpers) {
$array = array();
$helper = new Person\Person\View\Helper\PersonShowWidget($array);
return $helper;
},
)
);
}
Or in the module.config.php
'view_helpers' => array
(
'factories' => array(
'personShowWidget' => function ($helpers) {
$array = array();
$helper = new Person\Person\View\Helper\PersonShowWidget($array);
return $helper;
},
)
)
Performance-wise you'd better make a Factory class instead of a callable.
More info: http://framework.zend.com/manual/2.0/en/modules/zend.module-manager.module-manager.html
Edit:
It seems like you using the ViewHelper wrongly. You don't have to create the instance by yourself. Just use the ViewHelper in the view. So why not just give the $options as parameter to the __invoke method?
public function __invoke(array $options = null)
{
$this->setOptions($options);
return $this->getView()->render('person/show/show_widget', array(
'title' => 'Cliente',
'model' => $this->model,
)
);
}
In the Controller pass the options array to the view:
return array(
'options' => $options,
);
And call the ViewHelper in the view:
<?php echo $this->personShowWidget($this->options); ?>
Remember: In this way you don't need a Factory to init the ViewHelper. Just add it to the invokables.
module.config.php example:
'view_helpers' => array(
'invokables' => array(
'personShowWidget' => 'Person\Person\View\Helper\PersonShowWidget',
),
),

Yii CGridView handle special cases using model function

Having some trouble with CGridView on Yii Framework...
I'm looking to replace the contents of a column based on the value it holds.
I need to handle special cases so I added a function into the model to return a value to the GridView.
I get the resulting error " Undefined variable: model ".
I'm sure it's likely something simple. Is it because my dataProvider is not model?
Here is a shortened version of my code:
<?php
/* #var $this BookController */
/* #var $dataProvider CActiveDataProvider */
/* #var $model Book */
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'book-grid',
'dataProvider'=>$dataProvider,
'columns'=>array(
array(
'name'=>'userName',
'header'=>'Name',
),
array(
'name'=>'status',
'header'=>'Status',
'type'=>'raw',
'value'=>array($model, 'statusText')
),
)
));
?>
And here is code in models/Book.php
class Book extends CActiveRecord
{
...
...
public function statusText($data, $row) {
$content = '';
if (CHtml::encode($data->status) == "processed") {
$content = "Process completed";
}
else if ($data->status=="") {
$content = "Queued for Processing";
}
else {
$content = CHtml::encode($data->status);
}
return $content;
}
...
...
}
Here is a simplified example from my current project;
<?php
//My controller
class NewsController extends CController {
//The admin action
public function actionAdmin() {
$model = new News;
$this->render('admin', array(
'model' => $model
));
}
}
//In my view file
$this->widget('ext.widgets.MyTbGridView', array(
'dataProvider' => $model->search(),
'columns' => array(
array(
'name' => 'id',
'filter' => false,
),
array(
'name' => 'title',
),
array(
'value' => array($model, 'gridDate')
),
),
));
//My model function
class News extends CActiveRecord {
public function gridDate($data, $row) {
return 'Date formatted!';
}
}
?>
The code 'value' => array($model, 'gridFormatDate'), is important. there are two possibilities here. The function can reside in the controller, in which case it should be 'value' => array($this, 'gridFormatDate'), or it can be in the model, in which case the correct code is given
In stead of array($model, 'statusText'), try '$data->statusText'.
The method in your model should be like this:
public function getStatusText() {
$content = '';
if (CHtml::encode($this->status) == "processed") {
$content = "Process completed";
}
else if ($this->status=="") {
$content = "Queued for Processing";
}
else {
$content = CHtml::encode($this->status);
}
return $content;
}

Categories