PageRenderTime 53ms CodeModel.GetById 35ms app.highlight 16ms RepoModel.GetById 0ms app.codeStats 0ms

/guide/about.upgrading.md

https://github.com/HighwayofLife/userguide
Markdown | 313 lines | 194 code | 119 blank | 0 comment | 0 complexity | 107629ea21b62754762b0f492807134e MD5 | raw file
  1# Upgrading from 2.3.x
  2
  3Most of Kohana v3 works very differently from Kohana 2.3, here's a list of common gotchas and tips for upgrading.
  4
  5## Naming conventions
  6
  7The 2.x series used suffixes to differentiate between different 'types' of class (i.e. controller, model etc.).  Folders within model / controller folders didn't have any bearing on the name of the class.
  8
  9In 3.0 this approach has been scrapped in favour of the Zend framework filesystem conventions, where the name of the class is a path to the class itself, separated by underscores instead of slashes (i.e. `Some_Class_File` becomes `/some/class/file.php`).
 10
 11See the [conventions documentation](start.conventions) for more information.
 12
 13## Input Library
 14
 15The Input Library has been removed from 3.0 in favour of just using `$_GET` and `$_POST`.
 16
 17Some of its features, such as [value retrieval with defaults](#post_and_get), still exist in other forms.
 18
 19### XSS Protection {#xss_protection}
 20
 21If you need to XSS clean some user input you can use [Security::xss_clean] to sanitise it, like so:
 22
 23	$_POST['description'] = security::xss_clean($_POST['description']);
 24
 25You can also use the [Security::xss_clean] as a filter with the [Validate] library:
 26
 27	$validation = new Validate($_POST);
 28	
 29	$validate->filter('description', 'Security::xss_clean');
 30
 31### POST & GET {#post_and_get}
 32
 33One of the great features of the Input library was that if you tried to access the value in one of the superglobal arrays and it didn't exist the Input library would return a default value that you could specify i.e.:
 34
 35	$_GET = array();
 36	
 37	// $id is assigned the value 1
 38	$id = Input::instance()->get('id', 1);
 39	
 40	$_GET['id'] = 25;
 41	
 42	// $id is assigned the value 25
 43	$id = Input::instance()->get('id', 1);
 44
 45In 3.0 you can duplicate this functionality using [Arr::get]:
 46
 47	$_GET = array();
 48	
 49	// $id is assigned the value 1
 50	$id = Arr::get($_GET, 'id', 1);
 51	
 52	$_GET['id'] = 42;
 53	
 54	// $id is assigned the value 42
 55	$id = Arr::get($_GET, 'id', 1);
 56
 57You can of course combine this with the [XSS protection](#xss_protection) library to sanitise your input:
 58
 59	$input = Security::xss_clean(Arr::get($_POST, 'input', 'Default Value'));
 60
 61## ORM Library
 62
 63There have been quite a few major changes in ORM since 2.3, here's a list of the more common upgrading problems.
 64
 65### Member variables
 66
 67All member variables are now prefixed with an underscore (_) and are no longer accessible via `__get()`. Instead you have to call a function with the name of the property, minus the underscore.
 68
 69For instance, what was once `loaded` in 2.3 is now `_loaded` and can be accessed from outside the class via `$model->loaded()`.
 70
 71### Relationships
 72
 73In 2.3 if you wanted to iterate a model's related objects you could do:
 74
 75	foreach($model->{relation_name} as $relation)
 76
 77However, in the new system this won't work.   In version 2.3 any queries generated using the Database library were generated in a global scope, meaning that you couldn't try and build two queries simultaneously.  Take for example:
 78
 79# TODO: NEED A DECENT EXAMPLE!!!!
 80	
 81This query would fail as the second, inner query would 'inherit' the conditions of the first one, thus causing pandemonia.
 82In v3.0 this has been fixed by creating each query in its own scope, however this also means that some things won't work quite as expected.  Take for example:
 83
 84	foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
 85	{
 86		echo $post->title;
 87	}
 88
 89[!!] (See [the Database tutorial](tutorials.databases) for the new query syntax)
 90
 91In 2.3 you would expect this to return an iterator of all posts by user 3 where `post_date` was some time within the last 24 hours, however instead it'll apply the where condition to the user model and return a `Model_Post` with the joining conditions specified.
 92
 93To achieve the same effect as in 2.3 you need to rearrange the structure slightly:
 94
 95	foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
 96	{
 97		echo $post->title;
 98	}
 99
100This also applies to `has_one` relationships:
101
102	// Incorrect
103	$user = ORM::factory('post', 42)->author;
104	// Correct
105	$user = ORM::factory('post', 42)->author->find();
106
107### Has and belongs to many relationships
108
109In 2.3 you could specify `has_and_belongs_to_many` relationships.  In 3.0 this functionality has been refactored into `has_many` *through*.
110
111In your models you define a `has_many` relationship to the other model but then you add a `'through' => 'table'` attribute, where `'table'` is the name of your through table. For example (in the context of posts<>categories):
112
113	$_has_many = array
114	(
115		'categories' => 	array
116							(
117								'model' 	=> 'category', // The foreign model
118								'through'	=> 'post_categories' // The joining table
119							),
120	);
121
122If you've set up kohana to use a table prefix then you don't need to worry about explicitly prefixing the table.
123
124### Foreign keys
125
126If you wanted to override a foreign key in 2.x's ORM you had to specify the relationship it belonged to, and your new foreign key in the member variable `$foreign_keys`.
127
128In 3.0 you now define a `foreign_key` key in the relationship's definition, like so:
129
130	Class Model_Post extends ORM
131	{
132		$_belongs_to = 	array
133						(
134							'author' => array
135										(
136											'model' 		=> 'user',
137											'foreign_key' 	=> 'user_id',
138										),
139						);
140	}
141
142In this example we should then have a `user_id` field in our posts table.
143
144
145
146In has_many relationships the `far_key` is the field in the through table which links it to the foreign table and the foreign key is the field in the through table which links "this" model's table to the through table.
147
148Consider the following setup, "Posts" have and belong to many "Categories" through `posts_sections`.
149
150| categories | posts_sections 	| posts   |
151|------------|------------------|---------|
152| id		 | section_id		| id	  |
153| name		 | post_id			| title   |
154|			 | 					| content |
155
156		Class Model_Post extends ORM
157		{
158			protected $_has_many = 	array(
159										'sections' =>	array(
160															'model' 	=> 'category',
161															'through'	=> 'posts_sections',
162															'far_key'	=> 'section_id',
163														),
164									);
165		}
166		
167		Class Model_Category extends ORM
168		{
169			protected $_has_many = 	array (
170										'posts'		=>	array(
171															'model'			=> 'post',
172															'through'		=> 'posts_sections',
173															'foreign_key'	=> 'section_id',
174														),
175									);
176		}
177
178
179Obviously the aliasing setup here is a little crazy, but it's a good example of how the foreign/far key system works.
180
181### ORM Iterator
182
183It's also worth noting that `ORM_Iterator` has now been refactored into `Database_Result`.
184
185If you need to get an array of ORM objects with their keys as the object's pk, you need to call [Database_Result::as_array], e.g.
186
187		$objects = ORM::factory('user')->find_all()->as_array('id');
188
189Where `id` is the user table's primary key.
190
191## Router Library
192
193In version 2 there was a Router library that handled the main request.  It let you define basic routes in a `config/routes.php` file and it would allow you to use custom regex for the routes, however it was fairly inflexible if you wanted to do something radical.
194
195## Routes
196
197The routing system (now refered to as the request system) is a lot more flexible in 3.0. Routes are now defined in the bootstrap file (`application/bootstrap.php`) and the module init.php (`modules/module_name/init.php`). It's also worth noting that routes are evaluated in the order that they are defined.
198
199Instead of defining an array of routes you now create a new [Route] object for each route. Unlike in the 2.x series there is no need to map one uri to another. Instead you specify a pattern for a uri, use variables to mark the segments (i.e. controller, method, id).
200
201For example, in 2.x these regexes:
202
203	$config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
204
205Would map the uri `controller/id/method` to `controller/method/id`.  In 3.0 you'd use:
206
207	Route::set('reversed','(<controller>(/<id>(/<action>)))')
208			->defaults(array('controller' => 'posts', 'action' => 'index'));
209
210[!!] Each uri should have be given a unique name (in this case it's `reversed`), the reasoning behind this is explained in [the url tutorial](tutorials.urls).
211
212Angled brackets denote dynamic sections that should be parsed into variables. Rounded brackets mark an optional section which is not required. If you wanted to only match uris beginning with admin you could use:
213
214	Rouse::set('admin', 'admin(/<controller>(/<id>(/<action>)))');
215
216And if you wanted to force the user to specify a controller:
217
218	Route::set('admin', 'admin/<controller>(/<id>(/<action>))');
219	
220Optional route variables need default values in order to function correctly, you specify these as follows:
221
222	Route::set('reversed','(<controller>(/<id>(/<action>)))')
223		->defaults(array(
224			'controller' => 'post',
225			'action'     => 'list',
226			'id'         => 1
227		));
228
229[!!] Kohana has only one 'default default': Kohana assumes your default 'action' is 'index' unless you tell it otherwise, you can change this super-default by setting [Route::$default_action].
230
231If you need to use custom regex for uri segments then pass an array of `segment => regex` i.e.:
232
233	Route::set('reversed', '(<controller>(/<id>(/<action>)))', array('id' => '[a-z_]+'))
234			->defaults(array('controller' => 'posts', 'action' => 'index'))
235
236This would force the `id` value to consist of lowercase alpha characters and underscores.
237
238### Actions
239
240One more thing we need to mention is that methods in a controller that can be accessed via the url are now called "actions", and are prefixed with 'action_'.
241
242For example, in the above routes, if the user calls `admin/posts/1/edit` then the action is `edit` but the method called on the controller will be `action_edit`.  See [the url tutorial](tutorials.urls) for more info.
243
244## Sessions
245
246There are no longer any `Session::set_flash()`, `Session::keep_flash()` or `Session::expire_flash()` methods, instead you must use [Session::get_once].
247
248## URL Helper
249
250Only a few things have changed with the url helper
251
252* `url::redirect()` has been moved into [Request::redirect]
253
254	This instance function can be accessed by `$this->request->redirect()` within controllers and `Request::instance()->redirect()` globally.
255
256* `url::current` has now been replaced with `$this->request->uri()`
257
258## Valid / Validation
259
260These two classes have been merged into a single class called `Validate`.
261
262The syntax has also changed a little for validating arrays:
263
264	$validate = new Validate($_POST);
265	
266	// Apply a filter to all items in the arrays
267	$validate->filter(TRUE, 'trim');
268	
269	// To specify rules individually use rule()
270	$validate
271		->rule('field', 'not_empty')
272		->rule('field', 'matches', array('another_field'));
273	
274	// To set multiple rules for a field use rules(), passing an array of rules => params as the second argument
275	$validate->rules('field', 	array(
276									'not_empty' => NULL,
277									'matches'	=> array('another_field')
278								));
279
280The 'required' rule has also been renamed to 'not_empty' for clarity's sake.
281
282## View Library
283
284There have been a few minor changes to the View library which are worth noting.
285
286In 2.3 views were rendered within the scope of the controller, allowing you to use `$this` as a reference to the controller within the view, this has been changed in 3.0. Views now render in an empty scope. If you need to use `$this` in your view you can bind a reference to it using [View::bind]: `$view->bind('this', $this)`.
287
288It's worth noting, though, that this is *very* bad practice as it couples your view to the controller, preventing reuse.  The recommended way is to pass the required variables to the view like so:
289
290	$view = View::factory('my/view');
291	
292	$view->variable = $this->property;
293	
294	// OR if you want to chain this
295	
296	$view
297		->set('variable', $this->property)
298		->set('another_variable', 42);
299		
300	// NOT Recommended
301	$view->bind('this', $this);
302
303Because the view is rendered in an empty scope `Controller::_kohana_load_view` is now redundant.  If you need to modify the view before it's rendered (i.e. to add a generate a site-wide menu) you can use [Controller::after].
304	
305	Class Controller_Hello extends Controller_Template
306	{
307		function after()
308		{
309			$this->template->menu = '...';
310			
311			return parent::after();
312		}
313	}