Changes
Title
unchanged
Yii for beginners 2
Category
unchanged
Tutorials
Yii version
changed
1.1
Tags
changed
yYii, mvc,tutorial, beginner, MVC
Content
changed
Intro (Part 2)
------------------
Hi :-) Is that you again? Welcome !! :-)
This is **second** article with my tutorial. I had to add it
, because Wiki article has limited length and as I was extending it's text, older paragraphs were disappearing. So here I am starting from chapter 6.
Previous article can be found here: [**Yii for beginners 1**](http://www.yiiframework.com/wiki/250/yii-for-beginners/ "Yii for beginners 1").
If you are interested in Yii v2, check this out:
[Yii v2 for beginners](https://www.yiiframework.com/wiki/2552/yii-v2-for-beginners)
6. Your Own SQL
----------------
Sometimes it is much easier to create your own SQL query. I measured difference between ActiveRecord query and pure SQL. Pure SQL was 2x faster. Usefull mainly in ajax. So here is a small code that performs your SQL query and returns an array with results:
```php
public static function sqlQuery($sql)
{[...]
```php
return array(
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',[...]
```php
Yii::app()->theme = 'theme1'; // = each user can have (select) different theme
```[...]
Sometimes you want to offer a file to only a LOGGED IN user but don't want him to see it's path. Nobody has to know that you store files in folder: _myweb.com/files._ Usually links on webs look like this:
```php
<a href="myweb.com/files/my.pdf">download</a>
```[...]
It is also good to store file names in DB and save files to your storage only with their IDs. Like this:
```php
myweb.com/files/1.pdf
myweb.com/files/2.doc[...]
At the end, your download links will look like this:
```php
myweb.com/storage/download/123/how-to-do-something.pdf
```[...]
or rewritten like this:
```php
myweb.com/controller/action/GET-ParamName/GET-ParamValue
```[...]
Finally download links look like this:
```php
<a href="myweb.com/storage/download/123/how-to-do-something.pdf">download</a>
```[...]
The action is in Controller "storage" and is called actionDownload(). It processes GET parameter like this:
```php
$error = false;[...]
Now you just have to have a look into DB, find filename and path. When this is done, you just write this:
```php
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');[...]
And do not forget to put .htaccess file to your storage-folder with this content:
```php
SetHandler enginge_off
deny from all[...]
- To this file enter following text:
```php
<?php
class ForsuppliersModule extends CWebModule[...]
- In your project A (the parrent project) open file protected/config/main.php a find section 'modules'. Here you have to add your new module like this:
```php
'modules'=>array(
'forsuppliers'=>array()[...]
Go to protected/components/Controller.php and make sure there is constructor with cca following content:
```php
public function __construct($id, $module = null)
{[...]
In protected/config/main.php you need to add your source (basic) language. I recommend English.
```php
'sourceLanguage' => 'en_us',
```[...]
To filter posts by id_user I recommend to create a scope in the model that will return only posts of particular user. Scope can work with the session variable id_user. This scope can be named getCurrentUsersPosts. Filtering than looks like this:
```php
$posts = Post::model()->getCurrentUsersPosts()->findAll();
foreach ($posts as $post)[...]
```
By clicking one record, it’s ID is sent to server and it shows the detail of it. It works like this:
```php
$post = Post::model()->findByPk($_POST[“id_post”]);
```[...]
Just add here the scope:
```php
$post = Post::model()‐>getCurrentUsersPosts()‐>findByPk($_POST[“id_post”]);
```[...]
First, you need the SQL. Let's use some simple one:
```php
SELECT * FROM user
```
In your controller create a new action and insert following code. It is usual for CGridView:
```php
$model = new User('search');
if (isset($_GET['User']))[...]
In the view "userList" paste this definition of CGridView:
```php
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $model->search(),[...]
Let me remark that in the "value" section you can use any PHP code. Do not use ECHO nor RETURN. For example:
```php
'value'=>'convertMyNumber($data->getcount())',
```[...]
To disable filtering, just write to the column-definition this:
```php
'filter' => '',
```
Now you only have to define the search() method in your model. It will return data for CGridView. And you can of course rename this method.
```php
public function search()
{[...]
Sorting is defined using CSort object.
And to page records correctly you have to count how many records are totally available
```php
public function search()
{[...]
If you want to filter items you have to probably do it by your self. If any filter is used, then variable $model contains values that should be filtered. So you just have to use it and add/modify your final WHERE condition:
```php
$where = '';
$whereArray = array();[...]
```
If your query was:
```php
$sql = 'SELECT * FROM user ';
```
You just add this:
```php
$sql = 'SELECT * FROM user '.$where;
```[...]
As I already wrote many times, I'm friend of very simple native solutions. Following works for me well. It saves GET parameters and when you come back to the page, they are used to filter the grid. It of course has to distinguish what Grid ID (or particular page with set of grids) you are using, but the concept is fine I think. The only problem can appear when $_GET['ajax'] is saved and then used when it should not be.
```php
if (!isset(Yii::app()->session["lastGet"]))
{[...]
1) You can set different ID to each grid and do this:
```php
$upperModel = new Model();
$lowerModel = new Model();[...]
You will also need the search() method with cca this content:
```php
public $search_closed_value = 0;
public function search();[...]
Before calling this method, do not forget to set:
```php
$model->search_closed_value = 1; // If you are searching for closed records. Open records will be searched automatically.
```[...]
If you want to enable user to set different filters in the URL for both tables, it is very easy to enhance this example. The goal is to have different model-name, but the same logic. How to do it? .. Just create a second model that will extend the first model and will contain only 1 row.
```php
Class Model_closed extends
YourModel
{
public $search_closed_value = 1;[...]
Now you only have to enhance the action in point 1. You won't need the ID of the grid.
```php
$upperModel = new Model();
$lowerModel = new Model_closed();[...]
```php
public function sendHtmlAsXLS($html,$lastModified='', $filenameWithoutExtension='')
{[...]
Make sure that your config/main.php contains this:
```php
return array(
// application components[...]
Now edit file protected/extensions/PHPExcel/Autoloader.php and replace following method:
```php
public static function Register()
{[...]
I don't understand it but it works. Now you can create Excel like this:
```php
$objPHPExcel = new PHPExcel();
$objPHPExcel->removeSheetByIndex(0); // this removes the first sheet that is automatically created - usefull when generating excel in a loop[...]
If you write following code (in defaultScope), yii crashes:
```php
public function defaultScope()
{[...]
In defaultScope() you have to call it like this:
```php
$alias = $this->getTableAlias(false, false);
```[...]
Basic idea is to add "scopes" to definition of your relation. It should technically work, because method resetScope() exists in each model and returns $this. But in my case (Yii 1.1.15) it didn't.
```php
public function relations(){
return array([...]
To make this work, you have to override method resetScope() in the related model, add one variable and use it in your defaultScope(). This solved my situation.
```php
private $_ignoreDefaultScope = false;[...]
2nd = defaultScope()
3rd = custom scope
Each validator (see Yii2)
------------------------------------
If you need the "each validator" (for validating array of values) which is available in Yii2, you can paste this method to your model and use it as the validator. Do not forget to specify validation rules for the particular values as well! See examples below.
```php
public function each($attribute, $params)
{
$attr = $this->$attribute;
if (!is_array($attr)) {
$attr = [$attr];
}
$validatorName = $params['rule'][0];
$validatorParams = array_slice($params['rule'], 1);
$validatedAttributes = [$attribute];
$model = new self(); // Better would be to create a brand new model dynamically
$validator = CValidator::createValidator($validatorName, $model, $validatedAttributes, $validatorParams);
foreach ($attr as $value) {
$model->$attribute = $value;
$validator->validate($model, $validatedAttributes);
foreach ($model->getErrors($attribute) as $e) {
$this->addError($attribute, $e);
}
$model->clearErrors();
}
}
```
Usage is standard:
```php
public function rules()
{
return array(
array('myColumn', 'each', 'rule' => ['validatorName', 'optionalParamA' => 1]),
);
}
```
Where "validatorName" can be either an existing validator class or your custom validation method within your model. No difference here.
```php
public function validatorName($attribute, $params)
{
$val = $this->$attribute;
$paramA = $params['optionalParamA']??'Not set';
$this->addError($attribute, 'An error text');
}
```
This code can also be found in the documentation of [CValidator](https://www.yiiframework.com/doc/api/1.1/CValidator)