Cakephp Localization, Cannot Change language when DEFAULT_LANGUAGE is set - cakephp

I am confused :)
I'm using the p18n component in cakephp found here:
http://www.palivoda.eu/2008/04/i18n-in-cakephp-12-database-content-translation-part-2/
This component requires me to set in core.php the following constant:
define("DEFAULT_LANGUAGE", 'eng')
However when this is set I cannot change the language using:
Configure::write('Config.language', 'eng');
At the moment, into my knowledge, the only way to change the locale of my static content is the use of the Configure::write. But for the dynamic content to change through the use of the p28n component I must have the DEFINE_LANGUAGE constant set to a value.
This is all very confusing. Any help will be much appreciated.

I'm not familiar with particular component, but I've done this "manually" by setting the same constant in my app/config/bootstrap.php file and then setting the "actual" language to be used in my AppController (copied from the core code to app/app_controller.php). The appropriate snippets of that controller look like this:
uses ( 'L10n' );
class AppController extends Controller {
public function beforeFilter() {
$this->_setLanguage();
/**
* Set the default "domain" for translations. The domain is the
* same as the po file name in a given locale directory. e.g.
* __d ( 'homepage', 'message_id' ) would look for the
* message_id key in homepage.po. Using the __() convenience
* function will always look in default.po.
*/
$this->set ( 'domain', 'default' );
}
private function _setLanguage() {
$this->L10n = new L10n();
# Auto-detect the request language settings
$this->L10n->get();
}
}
Pretty vanilla stuff, but it works great. And breaking out the _setLanguage() method allows for the use of different methodologies to determine locale (e.g subdomain like fr.mydomain.com).

Related

How can i fetch dynamic data from database based on selected language.?

Hi i am working on a project in laravel 7.0, in back-end i have a table called Posts which contains 2 text language input one in french and the other is arabic added by the back-end application.
what i am trying to do is when the user uses the French Language i want the title_fr to be displayed on the view and same thing in Arabic language the title should be title_ar.
P.S data are stored in French and Arabic
I have tried the similar solution given in an other similar question but none of it worked in my case!
Any idea how i might get this to work ?
Thanks in advance.
You can do something similar to below. We have a model Post, this model has an attribute title. I also assume that you have an attribute that will return user's language from the User model.
class Post extends Model
{
public function getTitleAttribute(): string
{
return Auth::user()->language === 'fr' ? $this->title_fr : $this->title_ar;
}
}
FYI above is just a demo on what can be done. For a full blow solution I would recommend decorator pattern.
Also it might be worth considering using morph for things like that. You can have a service provider that will initiate the morph map for you post model relevant to the language that user has, I.e.
Class ModelProvider {
Protected $models = [
‘fr’ => [
‘post’ => App/Models/Fr/Post::class,
],
‘ar’ => [
‘post’ => App/Models/Ar/Post::class,
]
];
Public function boot() {
$language = Auth::user()->Settings->language;
Relation::morphMap($This->models[$language]);
}
}
Afterwards you just need to call to Relation::getMorphModel(‘post’) to grab Post class that will return correct language.
I.e. App/Models/Fr/Post can have a an attribute title:
Public function getTitleAttribute(): string {
Return $this->title_fr;
}
For example above you would also want to utilise interfaces to make sure that all models follow the same contract, something below would do the trick:
Interface I18nPostInterface {
Public function getTitleAttribute(): string
}
Also, depending on the database you use, to store titles (and other language data) in a JSON format in the database. MySQL 8 has an improve support for JSON data, but there are limitations with that.
So I was Able to fetch data from my database based on the Language selected by the user.
Like i said before I have a table called Posts and has columns id,title_fr and title_ar. I am using laravel Localization.
Inside my PostController in the index function i added this code:
public function index()
{
//
$post = Post::all();
$Frtitle = post::get()->pluck('title_fr');
$Artitle = post::get()->pluck('title_ar');
return view('post.index',compact('post','Frtitle','Artitle'));
}
if anyone has a better way then mine please let me know, i am sure
there is a better way.

Organize Models in subdirectories CakePHP 3

we are using subdirectories in our projects no separete views and controllers but in models we didn’t learn yet. Recently I’ve found this https://github.com/cakephp/cakephp/issues/60451 and actually routes and plugins we are already using, we just want to separete our models like this:
Model
-Entity
–Financial
—Money.php
-Table
–Financial
—MoneyTable.php
I’ve tryed put like this then controller is not able to find his model. How can I do to organize it, and make it work?
Things that we've tried:
Use $this->setAlias('TableModel');
Call in controller:
$this->TableModel = $this->loadModel('Subfolder/TableModel');
didn't work for SQL build, and other classes.
CakePHP uses the TableRegister to load models. That class can be configured to use a class that implements the LocatorInterface, and CakePHP uses the TableLocator as the default.
The only thing you can do is configure your own LocatorInterface instance in your bootstrap.php. You would have to create your MyTableLocator and have it change the className for tables to point to subdirectories. What rules for this class name rewritting are used is purely up to you.
bootstrap.php:
TableRegister::setTableLocator(new MyTableLocator());
MyTableLocator.php:
class MyTableLocator extends TableLocator {
protected function _getClassName($alias, array $options = [])
{
if($alias === 'Subfolder/TableModel') {
return TableModel::class;
}
return parent::_getClassName($alias, $options);
}
}
The above isn't working code.
I'm just demonstrating what the function is you need to override, and that you need logic in place to return a different class name.
You can check if the $alias contains the / character, and if so. Return a class name by extracting the subfolder name from the $alias. Take a look at the TableLocator to see how it's using the App::className function.

Cakephp Plugin to generate sitemap

I'm using Cakephp 2.4.3 . I've read that "There are CakePHP plugins that are able to generate sitemaps for you. This way your sitemap.xml file will be created dynamically on demand and will always be up to date." . I've searched but all I find are from old cakephp version which is not useful as they only cause errors .
Is there still a good plugin for this?
Some plugins definitely exist:
https://github.com/sdevore/cakephp-sitemap-plugin
https://github.com/smarek/Croogo-Sitemap-2.0
https://github.com/webtechnick/CakePHP-Seo-Plugin
Are these the old, error-causing ones? As each CakePHP site can be radically different to the next, I'm not sure a one-size-fits-all solution will exist.
If you end up writing your own sitemap implementation, it'll depend mainly on whether your site has:
Lots of database-driven content with few controllers/actions (like a typical WordPress-style site)
Lots of controller/action driven content (more of a web application)
In the first case, you'd want to perform finds on your content, and inject the results into an xml template like this: http://bakery.cakephp.org/articles/masterkeedu/2008/08/26/automatically-generate-dynamic-sitemaps
For the second case, the following may help: a component I've used for development/testing, which lists all controllers and their methods:
<?php //File: app/Controller/Component/CtrlComponent.php
// Component rewritten for Cake2.x, original from : http://cakebaker.42dh.com/2006/07/21/how-to-list-all-controllers/
class CtrlComponent extends Component {
/**
* Return an array of Controllers and their methods.
* The function will exclude ApplicationController methods
* #return array
*/
public function get() {
$aCtrlClasses = App::objects('controller');
foreach ($aCtrlClasses as $controller) {
if ($controller != 'AppController') {
// Load the controller
App::import('Controller', str_replace('Controller', '', $controller));
// Load its methods / actions
$aMethods = get_class_methods($controller);
foreach ($aMethods as $idx => $method) {
if ($method{0} == '_') {
unset($aMethods[$idx]);
}
}
// Load the ApplicationController (if there is one)
App::import('Controller', 'AppController');
$parentActions = get_class_methods('AppController');
$controllers[$controller] = array_diff($aMethods, $parentActions);
}
}
return $controllers;
}
}
In reality, a full sitemap probably uses both methods, and you'll need to consider the difference between public and "private" areas of your site (excluding admin prefixes, for example)..

How to use Ext.create properly

Can't find any relevant information in the sencha documention about this question :
Is it possible to call Ext.create(...) with a parameter which does not depend on the application's name?
So that if I change the app's name I don't have to rewrite that line of code?
Normally I would use Ext.create(AppName.model.MYMODEL) but that's too tied to the app's name for me.
Still need help :)
Create using class alias
When using Ext.define to define your class, you can provide an alias property. You've probably seen this on UI components which use aliases like widget.panel. Those aliases can be used with Ext.create.
Ext.define('MyApp.SomeClass', {
alias: 'app.someclass', // Independent of class name
/* ... */
});
Ext.create('app.someclass', {
/* ... */
});
You can set the alias on a class after it has been created by using Ext.ClassManager.setAlias.
Helper function using application name
If you don't have the option to set an alias, you could create a function that wraps Ext.create which supplies your base namespace automatically.
The problem here is that Ext.application doesn't return the application object. I'm not sure how Sencha Architect generates the application code but you may need additional overrides to allow you to retrieve the application object.
function appCreate(className, config) {
var appName = someMethodThatGetsTheApplicationName();
return Ext.create(appName + '.' + className, config);
};
// Example usage: Creates object of MyApp.model.MyModel
var myObj = appCreate('model.MyModel', { /* ... */ });
How to get the application name at runtime
By default, Ext JS does not retain a reference to the application object when using Ext.application, so we need an override to do it. I'm using Ext.currentApp as the property to store this object, but you can change it to whatever you'd like.
Ext.application = function (config) {
Ext.require('Ext.app.Application');
Ext.onReady(function () {
Ext.currentApp = new Ext.app.Application(config);
});
};
Now that you have this, you can access the application name by simply using Ext.currentApp.name. Or, if you'd feel more comfortable using a getter you can use the following.
Ext.app.Application.addMembers({
getName: function () {
return this.name;
}
});
// Example usage:
function someMethodThatGetsTheApplicationName() {
if (!Ext.currentApp) {
Ext.Error.raise('Current app does not exist.');
}
return Ext.currentApp.getName();
}
You can use any class name in Ext.create there is no naming convention imposed there as long as the class was already defined. If you want Ext.create to load the correct file using Ext.loader you will need to configure the loader to conform with the naming convention you need.
The way to do it :
You need a controller that will in it's INIT function (before UI Loading/Initiating) do the following
APPNAME = this.getApplication().getName();
Where APPNAME is a global variable.
Then when you Ext.create something you will be able to write the following
Ext.create(APPNAME +'model.MyModel');
That way you can change you app name without having to check everywhere in your code to change every single Ext.create to the new app's name.
It also give you the ability if you are to use this.getApplication().setName() to have infinite cache storage has you get 5/10mb per AppName.

how to set default prefix in controller -> skip defining prefix in links

I have the following idea: I'd like to be able to define the default prefix in any given controller. So let's say the default prefix for the CitiesController implements all actions with the "admin" prefix ("admin_index", "admin_add", etc.), but the ProvincesController implements all actions with the
"superadmin" prefix ("superadmin_index", "superadmin_add", etc.)
The problem with this is, every time I want to link to any "city stuff", I have to specify "admin" => "true". Any time I want to link to any "province stuff", I have to specify
"superadmin" => "true".
That's already quite a bit of work initially, but if I decided I wanted to change the prefix from "admin" to "superadmin" for cities, it would be even more work.
So I was wondering if there's to somehow do something along the lines of:
class CitiesController extends AppController {
var $defaultPrefix = "admin"
}
And then in the HTML helper link function, do something like:
class LinkHelper extends AppHelper {
public $helpers = array('Html');
function myDynamicPrefixLink($title, $options) {
// check whether prefix was set (custom function that checks all known prefixes)
if (! isPrefixSet($options)) {
// no clue how to get the controller here
$controller = functionToGetControllerByName($options['controller']);
// check whether controller has a defined default prefix
$prefix = $controller->defaultPrefix;
if ($prefix) {
// set the given prefix to true
$options[$prefix] = true;
}
// use HTML helper to get link
return $this->Html->link($title, $options);
}
}
I just have no clue how to get from the helper to the controller of the given name dynamically.
Another option would be to store the default prefix somewhere else, but for now I feel that the best place for this would be in any given controller itself.
Another idea would be to even have that look up function dependent on both, the controller and the action, and not just the controller.
You should be able to use the Router::connect to supply defaults (see code and documentation on Github: link) to specify default prefixes for certain controllers and even actions.
However, if you want more flexibility than the current Router provides, you can extend your use of the Router::connect by specifying an alternate Route class to use:
Router::connect(
'/path/to/route',
array('prefix' => 'superadmin'),
array('routeClass' => 'MyCustomRouter')
);
Examples of this can be seen in the CakePHP documentation.

Resources