I'm using Yii2 and I want to create a web application with the ability to perform fast searches.
For example, when I type characters in a textbox, results displayed.
It's easy with ajax when we have only one language but how about in multilingual mode?
First set up multi language for your site there is doc for this.
Best way of auto support multi language for your site is using cookies variable for language. You can set up language cookies from any action as
public function actionLanguage()
{
if (isset($_POST['lang'])) {
$language = $_POST['lang'];
if (($langaugeModel = \app\models\Langauge::findOne(['name' => $language])) !== null) {
$varLang = [
'id' => $langaugeModel->id,
'name' => $langaugeModel->name,
'iso1' => $langaugeModel->iso1,
'iso2' => $langaugeModel->iso2
];
$cookies = new Cookie([
'name' => 'lang',
'value' => json_encode($varLang),
]);
yii::$app->getResponse()->getCookies()->add($cookies);
return $this->goBack((!empty(Yii::$app->request->referrer) ? Yii::$app->request->referrer : null));
} else {
throw new NotFoundHttpException('The requested langauge does not exist.');
}
} else {
return $this->goBack((!empty(Yii::$app->request->referrer) ? Yii::$app->request->referrer : null));
}
}
Here what I did was i placed all the language support of site in database and generate necessary cookies variable and placed it on client browser.
Next set up be before request event of your yii2 site in config/web.php file as
'as beforeRequest' => [
'class' => 'app\components\MyBehavior',
],
then create components\Mybehaviou.php file and place this code
namespace app\components;
use yii;
use yii\base\Behavior;
class MyBehavior extends Behavior
{
public function events(){
return [
\yii\web\Application::EVENT_BEFORE_REQUEST => 'myBehavior',
];
}
public function myBehavior(){
if (\yii::$app->getRequest()->getCookies()->has('lang')) {
$langIso = 'sdn';
\yii::$app->language = $langIso;
$langaugeVar = \yii::$app->getRequest()->getCookies()->getValue('lang');
$langauge = json_decode($langaugeVar);
$langIso = $langauge->iso2;
\yii::$app->language = $langIso;
}
}
}
This create your site language which depends on client because it depends on cookies of client.
Then create your search controller according to site language(\yii::$app->language)
for ajax search you can use select2 Widget. you can find demo and configuration on this link
Related
I'm developing Prestashop module, it will export customer data and orders, it will contain hooks for customer synchronization, cart and order events - generally module which will be an integration with CRM-like service.
My module contains it's own views, made in vue.js - single page, async. There are register, login, settings, etc. pages. Communication with backend is made by GET/POST requests on {baseUrl}/mymodule/actionname routes and simple json responses which vue views depend on. Simply I need to create REST endpoints for my module, something like examples below.
Wordpress custom RestApi:
class RestApi
{
public function __construct()
{
add_action('rest_api_init', array(get_class($this),
'register_endpoints'));
}
public static function register_endpoints()
{
register_rest_route('mymodule', '/login', array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array('RestApi', 'login' ),
));
}
}
SugarCRM custom RestApi:
class ModuleRestApi extends SugarApi
{
public function registerApiRest()
{
return [
'moduleLogin' => [
'reqType' => 'POST',
'noLoginRequired' => true,
'path' => [
'mymodule', 'login'
],
'method' => 'login'
],
];
}
}
I cannot find similar solution in PrestaShop, there is no word about custom endpoints in presta docs, I tried to use FrontModuleControllers with friendly url's but it doesn't seem to work for me, it throws a lot of stuff in response which is useless for me and when I try to override init() method it requires a lot of stuff too to actually initiate the controller. I need simple REST solution where I can put logic for recieving data from my views, pass it to my CRM service and return json responses to my views. I don't need any more templates or views rendering, just routing for cummunication.
PrestaShop doesn't support this out of the box. You can however do it with a module and front controllers.
This is a basic example of doing it.
1. Module to register friendly URLs
class RestApiModule extends Module
{
public function __construct()
{
$this->name = 'restapimodule';
$this->tab = 'front_office_features';
$this->version = '1.0';
parent::__construct();
}
public function install()
{
return parent::install() && $this->registerHook('moduleRoutes');
}
public function hookModuleRoutes()
{
return [
'module-restapimodule-login' => [
'rule' => 'restapimodule/login',
'keywords' => [],
'controller' => 'login',
'params' => [
'fc' => 'module',
'module' => 'restapimodule'
]
]
];
}
}
2. Create an abstract REST controller
Create an abstract controller so that actual endpoints can extend from it. Create it in your module controllers folder lets name it AbstractRestController.php
abstract class AbstractRestController extends ModuleFrontController
{
public function init()
{
parent::init();
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
$this->processGetRequest();
break;
case 'POST':
$this->processPostRequest();
break;
case 'PATCH': // you can also separate these into their own methods
case 'PUT':
$this->processPutRequest();
break;
case 'DELETE':
$this->processDeleteRequest();
break;
default:
// throw some error or whatever
}
}
abstract protected function processGetRequest();
abstract protected function processPostRequest();
abstract protected function processPutRequest();
abstract protected function processDeleteRequest();
}
3. Create an actual front controller
Create the front controller in your module controllers/front folder and name it login.php.
require_once __DIR__ . '/../AbstractRestController.php';
class RestApiModuleLoginModuleFrontController extends AbstractRestController
{
protected function processGetRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'get'
]));
}
protected function processPostRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'post'
]));
}
protected function processPutRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'put'
]));
}
protected function processDeleteRequest()
{
// do something then output the result
$this->ajaxDie(json_encode([
'success' => true,
'operation' => 'delete'
]));
}
}
Install the module and now you can hit http://example.com/restapimodule/login and depending on the request type it's going to do whatever you want and you get back JSON response.
To add more endpoints add another module-restapimodule-endpointname entry into hookModuleRoutes array and a front controller that extends from AbstractRestController.
If you also want proper response codes etc. you're going to have to set headers with native php functions as PrestaShop afaik doesn't have any utilities to do it for you or use some kind of library.
Same goes for any other headers you might want to set such as content-type (by default it is text/html).
It is possible to use the Prestashop Webservice, that allows to add resources from modules. This solution could save some time in terms of standards and security.
The documentation regarding module resources in Prestashop Webservice is in this link:
https://webkul.com/blog/creating-prestashop-module-webservice-api/
I created new resources with this code:
class WebserviceRequest extends WebserviceRequestCore {
public static function getResources(){
$resources = parent::getResources();
// if you do not have class for your table
$resources['test'] = array('description' => 'Manage My API', 'specific_management' => true);
$resources['categoryecommerce'] = array('description' => 'o jacie marcin', 'class' => 'CategoryEcommerce');
$mp_resource = Hook::exec('addMobikulResources', array('resources' => $resources), null, true, false);
if (is_array($mp_resource) && count($mp_resource)) {
foreach ($mp_resource as $new_resources) {
if (is_array($new_resources) && count($new_resources)) {
$resources = array_merge($resources, $new_resources);
}
}
}
ksort($resources);
return $resources;
}
}
And new class:
class CategoryEcommerceCore extends ObjectModelCore {
public $category_id;
public $category_core_id;
public static $definition = array(
'table' => "category_ecommerce",
'primary' => 'category_id',
'fields' => array(
'category_core_id' => array('type' => self::TYPE_INT),
)
);
protected $webserviceParameters = array();
}
Webservice is override properly. My class WebserviceRequest is copying to
/override/classes/webservice/WebserviceRequest
but class isn't copying to /override/classes/ when i installing my module.
How to add new resourcess with own logic ? I want to add categories within relation to my table.
Regards
Martin
As soon as there is literally nothing regarding the API except Webkul tutorial... I tried to implement the "Webkul's" tutorial, but also failed. However seems that it's better to use hooks instead of overrides. I used my "reverse engineering skills" to determine the way to create that API, so-o-o-o, BEHOLD! :D
Let's assume you have a custom PrestaShop 1.7 module. Your file is mymodule.php and here are several steps.
This is an install method wich allows you to register the hook within database (you can uninstall and reinstall the module for this method to be executed):
public function install() {
parent::install();
$this->registerHook('addWebserviceResources');
return true;
}
Add the hook listener:
public function hookAddWebserviceResources($resources) {
$added_resources['test'] = [
'description' => 'Test',
'specific_management' => true,
];
return $added_resources;
}
That specific_management option shows you are going to use WebsiteSpecificManagement file instead of database model file.
Create WebsiteSpecificManagement file, called WebsiteSpecificManagementTest (Test - is CamelCased name of your endpoint). You can take the skeleton for this file from /classes/webservice/WebserviceSpecificManagementSearch.php. Remove everything except:
setObjectOutput
setWsObject
getWsObject
getObjectOutput
setUrlSegment
getUrlSegment
getContent (should return $this->output; and nothing more)
manage - you should rewrite it to return/process the data you want.
Add
include_once(_PS_MODULE_DIR_.'YOURMODULENAME/classes/WebserviceSpecificManagementTest.php');
to your module file (haven't figured out how to include automatically).
Go to /Backoffice/index.php?controller=AdminWebservice and setup the new "Auth" key for your application, selecting the test endpoint from the permissions list. Remember the key.
Visit /api/test?ws_key=YOUR_KEY_GENERATED_ON_STEP_4 and see the XML response.
Add &output_format=JSON to your URL to see the response in JSON.
You have to use something like $this->output = json_encode(['blah' => 'world']) within manage method at WebsiteSpecificManagementTest.
Hi i am developing a webapp where i have user slim framework for routing and twig for frontend templating. I am working on social login module where after authentication an want the user to be redirected to dashboard section. I got stuck at the point of rendering. What i have to do is to call the method named channels.list shown below from another method written below this method:
$app->get('/', function() use ($app){
$templateData = [
'pageTitle' => 'Channels'
];
$app->render("routes/channels/list/list.twig", $templateData);
})->name('channels.list');
but when i tried calling the channels.list from my login's get request i am getting loader error.
I am calling it in this way from my login request:
$app->get('/log-in/:type', function($type) use ($app, $hybridauth) {
if ($type == 'Facebook') {
$user = callSocial($app, $hybridauth); // function call for social login
$email = $user->email;
$displayName = $user->displayName;
$db = $app->db->users;
$userData = $db->find(array('email' => $email))->count();
if ($userData == 0) {
// insert user segment
$template = 'channels.list';
$app->render($app->urlFor($template));
} else {
//update the last login time
$template = 'channels.list';
$app->redirect($app->urlFor($template));
}
} else {
$templateData = [
'pageTitle' => 'Log In',
'UI' => [
'noSidebar' => true
]
];
$app->render("routes/log_in/log_in.twig", $templateData);
}
})->name('logIn');
Is there any other way to call or method to call. I hope the question got cleared because i an new to slim that uses twig as a templating engine.
Note: : I can not call the twig file associated with the channels list view as some pre processing is associated with the php file that renders the twig view.
Yii is giving me 404 Error if I declare an action like this:
SiteController.php
public function actionRegisterUser()
This is how I call it in the main.php
['label' => 'Register User', 'url' => ['/site/RegisterUser']],
I tried several different combinations. The only combination that will work is this naming convention in both places:
public function actionRegisteruser
'url' => ['/site/registeruser']
I used to work on another Yii project (Yii 1.0) and I could name my actions in camel case and call them without any problem. Do I need to turn on some sort of setting to do this?
I also tried playing with the rules of the Controller but that didn't solve anything.
In some cases you need camelcase link. For example, for SEO purposes (keep inbound links). You could create rewrite rule on web server side or add inline rule to URL manager on app side. Example:
'urlManager' => [
'rules' => [
'<controller:RegisterUser>/<action:\w+>'=>'register-user/<action>',
],
],
Also it's possible to write custom URL rule. Example:
namespace app\components;
use yii\web\UrlRuleInterface;
use yii\base\Object;
class CarUrlRule extends Object implements UrlRuleInterface
{
public function createUrl($manager, $route, $params)
{
if ($route === 'car/index') {
if (isset($params['manufacturer'], $params['model'])) {
return $params['manufacturer'] . '/' . $params['model'];
} elseif (isset($params['manufacturer'])) {
return $params['manufacturer'];
}
}
return false; // this rule does not apply
}
public function parseRequest($manager, $request)
{
$pathInfo = $request->getPathInfo();
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) {
// check $matches[1] and $matches[3] to see
// if they match a manufacturer and a model in the database
// If so, set $params['manufacturer'] and/or $params['model']
// and return ['car/index', $params]
}
return false; // this rule does not apply
}
}
And use the new rule class in the [[yii\web\UrlManager::rules]] configuration:
[
// ...other rules...
[
'class' => 'app\components\CarUrlRule',
// ...configure other properties...
],
]
You need to specify your action like this ['/site/register-user']. As documentation says about Inline Actions:
index becomes actionIndex, and hello-world becomes actionHelloWorld
I have a site in zend framework . Now I am making site in multiple language. For it I need to modify the url.
For example if sitename is www.example.com then i want to make it like
www.example.com/ch
www.example.com/fr
There can be some work around it that you can ask me to create a folder name ch and put a copy of code inside it. But for it I have to manage multiple folder when updating files on server.
What is the best or correct way to do it ?
My routs code is
public function _initRouter() {
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$routes = array(
'page' => new Zend_Controller_Router_Route('page/:slug', array('controller' => 'staticpage', 'action' => 'page', 'slug' => ''))
);
$router->addRoutes($routes);
}
Thanks
You have to add the language as parameter in your route(s). Here is an example: http://devzone.zend.com/1765/chaining-language-with-default-route/
You need a function to get a user choice of a language and a default language used if a user just starts with example.com.
You may want to get the current browser and Language Header from the users HTTP request.
Take a look at Zend_Locale and Zend_Translate.
You can use something like $locale = new Zend_Locale('browser'); to detect the users browser language.
Then look if Zend_Translate or your translation engine has the language available and set it to a cookie or session to store the date.
If the user then navigate to some language change site like example.com/?language=en you may want to set the locale based on the user choice and recheck if available in your translations.
If not, get back to original default language and present an error page or something like that.
If you want to get your Zend_Router urls to be language dependent, which might be a bad choice because of copy paste, backlinks or forum posts of your links, you need to add something before each route.
In my Applications i use something like the following in my main bootstrap.php file. I've cut some parts of to keep it simple.
protected function _initTranslate() {
$session = new Zend_Session_Namespace("globals");
// Get current registry
$registry = Zend_Registry::getInstance();
/**
* Set application wide source string language
* i.e. $this->translate('Hallo ich bin ein deutscher String!');
*/
if(!$session->current_language) {
try {
$locale = new Zend_Locale('browser'); //detect browser language
}
catch (Zend_Locale_Exception $e) {
//FIXME: get from db or application.ini
$locale = new Zend_Locale('en_GB'); //use the default language
}
}
else {
$locale = new Zend_Locale($session->current_language);
}
$translate = new Zend_Translate(
array(
'adapter' => 'array',
'content' => realpath(APPLICATION_PATH . '/../data/i18n/'),
'locale' => $locale,
'scan' => Zend_Translate::LOCALE_DIRECTORY,
'reload' => false,
'disableNotices' => true, //dont get exception if language is not available
'logUntranslated' => false //log untranslated values
)
);
if(!$translate->isAvailable($locale->getLanguage())) {
$locale = new Zend_Locale('en_GB'); //default
}
$translate->setLocale($locale);
$session->current_language = $locale->getLanguage();
//Set validation messages
Zend_Validate_Abstract::setDefaultTranslator($translate);
//Max lenght of Zend_Form Error Messages (truncated with ... ending)
Zend_Validate::setMessageLength(100);
//Zend_Form Validation messages get translated into the user language
Zend_Form::setDefaultTranslator($translate);
/**
* Both of these registry keys are magical and makes do automagical things.
*/
$registry->set('Zend_Locale', $locale);
$registry->set('Zend_Translate', $translate);
return $translate;
}
This is for the default translation setup of each visitor.
To set a user language depending on some HTTP parameters, I decided to create a Plugin, which will run on each request, see if the global language parameter is set (key=_language) and try setting the new language.
I then redirect the user to the new route, depending on his choice.
So, if the user click on the link for english language (example.com/de/test/123?_language=en) he will get redirected to example.com/en/test/123.
class Application_Plugin_Translate extends Core_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
$frontController = Zend_Controller_Front::getInstance();
// Get the registry object (global vars)
$registry = Zend_Registry::getInstance();
// Get our translate object from registry (set in bootstrap)
$translate = $registry->get('Zend_Translate');
// Get our locale object from registry (set in bootstrap)
$locale = $registry->get('Zend_Locale');
// Create Session block and save the current_language
$session = new Zend_Session_Namespace('globals');
//get the current language param from request object ($_REQUEST)
$language = $request->getParam('_language',$session->current_language);
// see if a language file is available for translate (scan auto)
if($translate->isAvailable($language)) {
//update global locale
$locale = $registry->get('Zend_Locale');
$locale->setLocale($language);
$registry->set('Zend_Locale', $locale);
//update global translate
$translate = $registry->get('Zend_Translate');
$translate->setLocale($locale);
$registry->set('Zend_Translate', $translate);
//language changed
if($language!=$session->current_language) {
//update session
$session->current_language = $language;
$redirector = new Zend_Controller_Action_Helper_Redirector;
$redirector->gotoRouteAndExit(array());
}
}
else {
$request->setParam('_language', '');
unset($session->current_language);
$redirector = new Zend_Controller_Action_Helper_Redirector;
$redirector->gotoRouteAndExit(array());
}
}
}
And finally, to prepare your router with the new language routes, you need to setup a base language route and chain your other language depending routes.
public function _initRouter() {
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$languageRoute = new Zend_Controller_Router_Route(
':language',
array(
'language' => "de"
),
array('language' => '[a-z]{2}')
);
$defaultRoute = new Zend_Controller_Router_Route(
':#controller/:#action/*',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index'
)
);
$router->addRoute(
'default',
$defaultRoute
);
$languageDefaultRoute = $languageRoute->chain($defaultRoute);
$router->addRoute(
'language',
$languageDefaultRoute
);
}
Good luck with your project, hope it will help you and others!