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.
Related
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.
How can I prevent links automaticly from beeing displayed in the Template ctp files?
I will give you an example:
User(id = 1) is allowed to see teamcalendars/view/1
User(id = 2) is not allowed to see teamcalendars/view/1.
User1 is member of the team 1 and should see and follow the link. User2 is not member in any teams and neither should see the link to the calender nor follow it. But I would like to place the link in the teams/index file where both users can go to and see all teams, but with different options per team.
If User2 follows the link (or types it into the browser manually), the controller will return a redirect and a error message about missing privileges. User2 will anyhow never get there. But how do I prevent cake from displaying the link for User2 (its missleading)?
Is there a possibility to connect the link to the controller and action and the id of the object where it is leading to, so I don't neet to take care of building and passing variables for each view just to decide which links can be displayed?
Sorry for not providing any code, but I think anyone knows how to send an array from the Controller to the View and how to validate it with if(){echo $this->Html->link()}, which is what I am doing currently.
Thank you for any help or remarks in advance.
One option is to define your own HtmlHelper and override the link function, such that it checks the permissions on the link first and only outputs it if they are allowed access. Something like the following:
namespace App\View\Helper;
use \Cake\View\Helper\HtmlHelper;
// Or, if you're already using a third-party HTML helper, something like
// use BootstrapUI\View\Helper\HtmlHelper as HtmlHelper;
class MyHtmlHelper extends HtmlHelper
{
function link($title, $url = null, array $options = [])
{
if (checkMyPermissions($url)) {
return parent::link($title, $url, $options);
}
}
}
And then in your AppController:
use App\View\Helper\MyHtmlHelper;
public $helpers = [
'Html' => ['className' => 'MyHtmlHelper'],
// ... and all your other helpers
];
I have made a custom afterFind function in a model, but I just want it to execute it if NOT in admin mode.
public function afterFind($results) {
if(Configure::read('Routing.admin')){
return $results;
}else{
return $this->locale(&$results);
}
}
But it doesn't seems to work. I'm thinking this might not be possible. Any idea?
checking on the core Configure settings doesnt make sense to me.
besides the fact that that 'Routing.admin' is deprecated - its Prefix.admin.
it only stores the prefixes that cake uses.
If you really want to you can store the information in configure::read() in beforeFilter() of your AppController and read it from your model again.
But it would need to something that does not conflict with your settings.
So if you use Prefix you probably could use Routing again:
//beforeFilter - prior to any model find calls!
$isAdmin = !empty($this->params['admin']);
Configure::write('Routing.admin', $isAdmin);
the other option you always have is to pass the information on to the model.
Router::getParam('prefix', true) gives you current request prefix value.
public function afterFind($results, $primary = false) {
return Router::getParam('prefix', true) == 'admin' ? $results : $this->locale(&$results);
}
Tested with Cake 2.4.
I'm working on a small CakePHP application that is subject to the following constraint (awkward but out of my control): I need it to work on either of two identical databases, with the choice being based on URL. For example:
http://www.example.com/myapp/foo/action/param
http://www.example.com/myapp/bar/action/param
The first obvious solution is to have two identical CakePHP applications at myapp/foo and myapp/bar with different database configurations. This has a kludgy feel to it, though, so I'm trying to find an elegant way of creating a single application.
The approach I'm considering is this: Define routes such that myapp/foo and myapp/bar will be associated with the same controller. Then give my DATABASE_CONFIG class a constructor:
function __construct() {
$pathParts = explode('/', $_SERVER['REQUEST_URI']);
if (array_search('foo', $pathParts)) {
$this->default = $this->fooConfig;
} else if (array_search('bar', $pathParts)) {
$this->default = $this->barConfig;
}
}
(Where of course I've defined fooConfig and barConfig for the two databases.) I do have control over the URL, so I can be confident that there won't be extraneous occurrences of foo or bar in the URL.
My question is this: Is there a simpler, more elegant way of handling this odd situation? Maybe something in AppModel and/or AppController? Although I'm getting rid of duplicated code, I can't shake the feeling that I'm replacing one code smell with another.
There are a few ways to do this, here is one.
Write a sweet custom route in which you always match:
Router::connect('/:ds/*', array(), array('routeClass' => 'SweetDbRoute'));
Then have SweetDbRoutes set a class variable you can use everywhere, including in your model constructors. Then it should fail so you don't actually adjust the request.
App::import('SweetDbClass', array('file' => '/path/to/your/sweet_db_class.php'));
class SweetDbRoute extends CakeRoute {
// put your failing route code here, but use your SweetDbClass to track datasource ...
// see http://book.cakephp.org/view/1634/Custom-Route-classes
}
Then in your AppModel:
App::import('SweetDbClass', array('file' => '/path/to/your/sweet_db_class.php'));
class AppModel extends Model {
public function __construct($id = false, $table = null, $ds = null) {
$ds = SweetDbClass::$ds;
parent::__construct($id, $table, $ds);
}
}
So for example, after you perform an insert in one database, the two won't be "identical", right? Are these 2 DB somehow synced with each other? I don't know what do you need to do on those DB, but it's probably easier just to do 2 separate apps.
Yes, you can specify the DB configuration in the model: http://book.cakephp.org/view/922/Database-Configuration but you can't change it on-the-fly though (the models are not expected to change association to another table, I suppose). What you do is probably the only way.
I do have control over the URL, so I can be confident that there won't be extraneous occurrences of foo or bar in the URL
Yes, there can be "extraneous occurrences of foo or bar in the URL" :)) But it won't break your app.
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).