Yii Api Version urlManager w/ parseUrl - php

Im new with Yii and Im trying to implement the parseUrl to manage API Version at the url.
I would like to redirect /api/1.0/... to controller apiV1.0.
Main.php (urlManager rules):
...
array('class' => 'application.components.ApiVersion'),
...
ApiVersion.php:
<?php
class ApiVersion extends CBaseUrlRule
{
public $connectionID = 'db';
public function createUrl($manager,$route,$params,$ampersand)
{
if ($route==='car/index')
{
if (isset($params['manufacturer'], $params['model']))
return $params['manufacturer'] . '/' . $params['model'];
else if (isset($params['manufacturer']))
return $params['manufacturer'];
}
return false; // this rule does not apply
}
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
{
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
$url = $match[1] . $match[2]; // BUILD YOUR URL (controllerName/actionName)
$this->redirect(array($url));
}
return false;
}
}
Im very lost with how it works, someone could help me?

In your function the regex checks if the path is something like "api/num_of_version/your_model/an_id"
So if your url is matching this your are in the condition, you'll have to set the get values:
$_GET['apiversion'] = $matches[2];
$_GET['model'] = $matches[4];
$_GET['id'] = $matches[6];
And depending on the version you return the good controller:
return "apiV" . $_GET['apiversion'] . "/view";
Some links you need to understand:
How preg_match works (what is the structure of $matches)
Yii custom Url class
Edit: If there is a particular point where your have some trouble please let me now, i'll try to ellaborate a little more my answer on this point.

Related

phpunit how to test a method which uses the browser url

I have the following methods:
public function isAdmin()
{
$adminUrl = $this->getPage(2)->httpUrl;
/*
* Remove all segments except the first in the URL path
* to detect if current page is in admin view
*/
$currentUrl = preg_replace( // https://regex101.com/r/D0mOz9/1
sprintf(
'~(?<adminUrl>%s)(?<remove>.*)$~',
addslashes($adminUrl)
),
'$1',
$this->getPage()->httpUrl
);
return $adminUrl == $currentUrl || (!$currentUrl && !$this->getPage() instanceof NullPage);
}
public function getPage($page = null)
{
$wiredPage = $this->wire('page');
if (!is_null($wiredPage) && is_null($page)) {
$baseUrl = $this->wire('page')->url;
$urlSegmentStr = $this->wire('input')->urlSegmentStr;
if (strlen($urlSegmentStr)) {
$baseUrl = rtrim($baseUrl, '/') . "/$urlSegmentStr/";
}
return $this->pages->get($baseUrl);
}
if (is_null($page)) {
return $this->pages->get(parse_url(getenv('REQUEST_URI'), PHP_URL_PATH));
}
return $this->pages->get($page);
}
I check if for an admin page by getting the url to the admin page and compare it to the current url.
Now I want to write a test for it with PHPunit but I have no idea how to test a method which result is based on urls like this.
Is it possible to write a test for a case like this? How would I be able to do this?

Yii and multilingual URLs

I'm trying to create multilingual application. I've implemented ability of translationable content and next step should be showing it to user. I want to have ability of changing language depending on URL. I've found a couple of components for those purposes but they all create urls which I don't like. For example, my application's default language is English and I have content which is translated into French. I have page "contacts", for instance. And URLs which will be generated by application will be: mysite.com/en/contacts, mysite.com/fr/contacts, but I want to have mysite.com/contacts for default language and mysite.com/fr/contacts for French language. It's simillar for site's root too. mysite.com/ - for default language and mysite.com/fr for French.
Is there any methods for implementing these functionality?
I'm using XUrlManager extension XUrlManager on GitHub
Yii generates URL's based on UrlManager rules. If you want URL's without /lang/ code - you need just create correct rules. For example, if you dublicate records in rules array:
'rules'=>array(
'<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
'<language:\w{2}>/<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
);
your URL's will be generated withou /en/ and /fr/, but URL's with code works too. By default, XUrlManager use previously selected language and store this in session or cookie.
If you want only hide /en/ and use /fr/ and others always, you can change your XUrlManager extension with:
public function createUrl($route,$params=array(),$ampersand='&')
{
if(!isset($params['language']) && Yii::app()->language!=='en')
$params['language']=Yii::app()->language;
return parent::createUrl($route,$params,$ampersand);
}
I've found very elegant method for solving my problem on http://www.elisdn.ru
Reimplement CHttpRequest
class DLanguageHttpRequest extends CHttpRequest
{
private $_requestUri;
public function getRequestUri()
{
if ($this->_requestUri === null)
$this->_requestUri = DMultilangHelper::processLangInUrl(parent::getRequestUri());
return $this->_requestUri;
}
public function getOriginalUrl()
{
return $this->getOriginalRequestUri();
}
public function getOriginalRequestUri()
{
return DMultilangHelper::addLangToUrl($this->getRequestUri());
}
}
Reimplement CUrlManager
class DLanguageUrlManager extends CUrlManager
{
public function createUrl($route, $params=array(), $ampersand='&')
{
$url = parent::createUrl($route, $params, $ampersand);
return DMultilangHelper::addLangToUrl($url);
}
}
Change config
return array(
'sourceLanguage'=>'en',
'language'=>'ru',
'components'=>array(
'request'=>array(
'class'=>'DLanguageHttpRequest',
...
),
'urlManager'=>array(
'class'=>'DLanguageUrlManager',
...
),
),
...
'params'=>array(
'translatedLanguages'=>array(
'ru'=>'Russian',
'en'=>'English',
'de'=>'Deutsch',
),
'defaultLanguage'=>'ru',
),
);
Create DMultilangHelper
class DMultilangHelper
{
public static function enabled()
{
return count(Yii::app()->params['translatedLanguages']) > 1;
}
public static function suffixList()
{
$list = array();
$enabled = self::enabled();
foreach (Yii::app()->params['translatedLanguages'] as $lang => $name)
{
if ($lang === Yii::app()->params['defaultLanguage']) {
$suffix = '';
$list[$suffix] = $enabled ? $name : '';
} else {
$suffix = '_' . $lang;
$list[$suffix] = $name;
}
}
return $list;
}
public static function processLangInUrl($url)
{
if (self::enabled())
{
$domains = explode('/', ltrim($url, '/'));
$isLangExists = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages']));
$isDefaultLang = $domains[0] == Yii::app()->params['defaultLanguage'];
if ($isLangExists && !$isDefaultLang)
{
$lang = array_shift($domains);
Yii::app()->setLanguage($lang);
}
$url = '/' . implode('/', $domains);
}
return $url;
}
public static function addLangToUrl($url)
if (self::enabled())
{
$domains = explode('/', ltrim($url, '/'));
$isHasLang = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages']));
$isDefaultLang = Yii::app()->getLanguage() == Yii::app()->params['defaultLanguage'];
if ($isHasLang && $isDefaultLang)
array_shift($domains);
if (!$isHasLang && !$isDefaultLang)
array_unshift($domains, Yii::app()->getLanguage());
$url = '/' . implode('/', $domains);
}
return $url;
}
}
After all of these steps your application will have URLs which you want
More information here

CodeIgniter returning different request format / content type html|json|xml etc

What is a simple way in CodeIgniter that I can return a specific content type for request URL extension? For example I want to return json if the url is http://example.com/phone/digits/1.json, html if the URL ends in /1 or /1.html, and XML if the URL ends in /1.xml. This will load a view in the format specified. So in the above example (phone/digits/1.json) would return the json version of the digits method. Here is what I've got so far that is NOT correctly working but gives an idea of what I'm going for. It's currently generating a 404 if no arguments are passed (/phone/digits.json)... Any suggestions would be appreciated.
class Phone extends CI_Controller {
public $layout = FALSE;
public function __construct()
{
if (preg_match('/\.(html|json)$/', $ci->uri->uri_string(), $matches))
{
$this->format = ('html' == $matches[1] || !isset($matches[1])) ? '' : '.json.php';
}
}
public function digits()
{
$this->load->view('phone/digits' . $this->format);
}
Updated for clarity,
I didint understand your question well, asuming you want to simplify your url
its solution for this url : http://domain.com/phone/digits.json (xml or html also)
But with few modification , it can be useful also with http://domain.com/phone/digits/n.json (n - id number)
in config/routes.php
$route['phone/digits.(json|html|xml|php)'] = 'Phone/digits/$1';
$route['phone/digits'] = 'Phone/digits';
Controller
class Phone extends CI_Controller {
public $layout = FALSE;
public function __construct()
{
//hmm
}
public function digits($format = '')
{
if($format == '') {
//default view or something else
}
else {
$this->load->view('phone/digits' . $format);
}
}
Parse the url
grab the extension
use a switch condtional and set relevant output
-
switch( $extension ){
case 'json':
$this->output
->set_content_type('application/json')
->set_output(json_encode(array('foo' => 'bar')));
break;
case 'xml':
$this->output
->set_content_type('application/xml')
->set_output(file_get_contents(some_xml_file.xml));
break;
// etc etc
}

Yii r1.8 Using Custom URL Rule Classes

I am trying to take advantage of the Custom URL Rule Classes in Yii r1.8
I am trying to take something that looks like orgs/view/id/24 and instead display the name of the org as identified by Name in the db (i.e. changing www.mysite.com/orgs/view/id/24 to www.mysite.com/jaysshop dynamically ). Unfortunately I am not getting it to work.
Here is my code:
class OrgsUrlRule extends CBaseUrlRule
{
public $connectionID = 'db';
public function createUrl($manager,$route,$params,$ampersand)
{
if ($route==='orgs/view/id') //even tried 'orgs/view' or 'orgs/index'
{
if (isset($params['Name']))
return $params['Name'];
else if (isset($params['Name']))
return $params['Name'];
}
return false;
}
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
{
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
{
}
return false;
}
}
urlManager:
array(
'class' => 'application.components.OrgsUrlRule',
'connectionID' => 'db',
),
I'm giving you this example in the assumption that you want to take the name of a shop from the URL trigger the your controller/action on it
For example:
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
{
$aParts = explode('/', $pathInfo);
if (count($aParts) == 1) // It's only 1 piece, so a possible "shop name"
{
if (isAValidNameForAShop($aParts[0]))
{
$_REQUEST['id'] = $aParts[0]; // Store it to retrieve it in the controller
return 'orgs/view';
}
}
return FALSE; // Seems like something else, we don't apply
}
Hope that helps you along a bit.

An example of an MVC controller

I have been reading a lot about how and why to use an MVC approach in an application. I have seen and understand examples of a Model, I have seen and understand examples of the View.... but I am STILL kind of fuzzy on the controller. I would really love to see a thorough enough example of a controller(s). (in PHP if possible, but any language will help)
Thank you.
PS: It would also be great if I could see an example of an index.php page, which decides which controller to use and how.
EDIT: I know what the job of the controller is, I just don't really understand how to accomplish this in OOP.
Request example
Put something like this in your index.php:
<?php
// Holds data like $baseUrl etc.
include 'config.php';
$requestUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$requestString = substr($requestUrl, strlen($baseUrl));
$urlParams = explode('/', $requestString);
// TODO: Consider security (see comments)
$controllerName = ucfirst(array_shift($urlParams)).'Controller';
$actionName = strtolower(array_shift($urlParams)).'Action';
// Here you should probably gather the rest as params
// Call the action
$controller = new $controllerName;
$controller->$actionName();
Really basic, but you get the idea... (I also didn't take care of loading the controller class, but I guess that can be done either via autoloading or you know how to do it.)
Simple controller example (controllers/login.php):
<?php
class LoginController
{
function loginAction()
{
$username = $this->request->get('username');
$password = $this->request->get('password');
$this->loadModel('users');
if ($this->users->validate($username, $password))
{
$userData = $this->users->fetch($username);
AuthStorage::save($username, $userData);
$this->redirect('secret_area');
}
else
{
$this->view->message = 'Invalid login';
$this->view->render('error');
}
}
function logoutAction()
{
if (AuthStorage::logged())
{
AuthStorage::remove();
$this->redirect('index');
}
else
{
$this->view->message = 'You are not logged in.';
$this->view->render('error');
}
}
}
As you see, the controller takes care of the "flow" of the application - the so-called application logic. It does not take care about data storage and presentation. It rather gathers all the necessary data (depending on the current request) and assigns it to the view...
Note that this would not work with any framework I know, but I'm sure you know what the functions are supposed to do.
Imagine three screens in a UI, a screen where a user enters some search criteria, a screen where a list of summaries of matching records is displayed and a screen where, once a record is selected it is displayed for editing. There will be some logic relating to the initial search on the lines of
if search criteria are matched by no records
redisplay criteria screen, with message saying "none found"
else if search criteria are matched by exactly one record
display edit screen with chosen record
else (we have lots of records)
display list screen with matching records
Where should that logic go? Not in the view or model surely? Hence this is the job of the controller. The controller would also be responsible for taking the criteria and invoking the Model method for the search.
<?php
class App {
protected static $router;
public static function getRouter() {
return self::$router;
}
public static function run($uri) {
self::$router = new Router($uri);
//get controller class
$controller_class = ucfirst(self::$router->getController()) . 'Controller';
//get method
$controller_method = strtolower((self::$router->getMethodPrefix() != "" ? self::$router->getMethodPrefix() . '_' : '') . self::$router->getAction());
if(method_exists($controller_class, $controller_method)){
$controller_obj = new $controller_class();
$view_path = $controller_obj->$controller_method();
$view_obj = new View($controller_obj->getData(), $view_path);
$content = $view_obj->render();
}else{
throw new Exception("Called method does not exists!");
}
//layout
$route_path = self::getRouter()->getRoute();
$layout = ROOT . '/views/layout/' . $route_path . '.phtml';
$layout_view_obj = new View(compact('content'), $layout);
echo $layout_view_obj->render();
}
public static function redirect($uri){
print("<script>window.location.href='{$uri}'</script>");
exit();
}
}
<?php
class Router {
protected $uri;
protected $controller;
protected $action;
protected $params;
protected $route;
protected $method_prefix;
/**
*
* #return mixed
*/
function getUri() {
return $this->uri;
}
/**
*
* #return mixed
*/
function getController() {
return $this->controller;
}
/**
*
* #return mixed
*/
function getAction() {
return $this->action;
}
/**
*
* #return mixed
*/
function getParams() {
return $this->params;
}
function getRoute() {
return $this->route;
}
function getMethodPrefix() {
return $this->method_prefix;
}
public function __construct($uri) {
$this->uri = urldecode(trim($uri, "/"));
//defaults
$routes = Config::get("routes");
$this->route = Config::get("default_route");
$this->controller = Config::get("default_controller");
$this->action = Config::get("default_action");
$this->method_prefix= isset($routes[$this->route]) ? $routes[$this->route] : '';
//get uri params
$uri_parts = explode("?", $this->uri);
$path = $uri_parts[0];
$path_parts = explode("/", $path);
if(count($path_parts)){
//get route
if(in_array(strtolower(current($path_parts)), array_keys($routes))){
$this->route = strtolower(current($path_parts));
$this->method_prefix = isset($routes[$this->route]) ? $routes[$this->route] : '';
array_shift($path_parts);
}
//get controller
if(current($path_parts)){
$this->controller = strtolower(current($path_parts));
array_shift($path_parts);
}
//get action
if(current($path_parts)){
$this->action = strtolower(current($path_parts));
array_shift($path_parts);
}
//reset is for parameters
//$this->params = $path_parts;
//processing params from url to array
$aParams = array();
if(current($path_parts)){
for($i=0; $i<count($path_parts); $i++){
$aParams[$path_parts[$i]] = isset($path_parts[$i+1]) ? $path_parts[$i+1] : null;
$i++;
}
}
$this->params = (object)$aParams;
}
}
}
Create folder structure
Setup .htaccess & virtual hosts
Create config class to build config array
Controller
Create router class with protected non static, with getters
Create init.php with config include & autoload and include paths (lib, controlelrs,models)
Create config file with routes, default values (route, controllers, action)
Set values in router - defaults
Set uri paths, explode the uri and set route, controller, action, params ,process params.
Create app class to run the application by passing uri - (protected router obj, run func)
Create controller parent class to inherit all other controllers (protected data, model, params - non static)
set data, params in constructor.
Create controller and extend with above parent class and add default method.
Call the controller class and method in run function. method has to be with prefix.
Call the method if exisist
Views
Create a parent view class to generate views. (data, path) with default path, set controller, , render funcs to
return the full tempalte path (non static)
Create render function with ob_start(), ob_get_clean to return and send the content to browser.
Change app class to parse the data to view class. if path is returned, pass to view class too.
Layouts..layout is depend on router. re parse the layout html to view and render
Please check this:
<?php
global $conn;
require_once("../config/database.php");
require_once("../config/model.php");
$conn= new Db;
$event = isset($_GET['event']) ? $_GET['event'] : '';
if ($event == 'save') {
if($conn->insert("employee", $_POST)){
$data = array(
'success' => true,
'message' => 'Saving Successful!',
);
}
echo json_encode($data);
}
if ($event == 'update') {
if($conn->update("employee", $_POST, "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Update Successful!',
);
}
echo json_encode($data);
}
if ($event == 'delete') {
if($conn->delete("employee", "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Delete Successful!',
);
}
echo json_encode($data);
}
if ($event == 'edit') {
$data = $conn->get("select * from employee where id={$_POST['id']};")[0];
echo json_encode($data);
}
?>

Categories