Symfony CMF RoutingBundle - PHPCR Route Document - Multiple Parameters - url-routing

Tried to find a solution, but I got always stuck a the docs or at answers include other bundles. In the documentation of the dynamic router you can find the hint:
"Of course you can also have several parameters, as with normal Symfony routes. The semantics and rules for patterns, defaults and requirements are exactly the same as in core routes."
Thats it.
...
/foo/{id}/bar
I tried (seems not) everything to get it done.
Same for all tries:
I tried it to apply a variable pattern and a child route.
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route as PhpcrRoute;
$dm = $this->get('cmf_routing.route_provider');
$route = new PhpcrRoute();
$route->setPosition( $dm->find( null, '/cms/routes' ), 'foo' );
$route->setVariablePattern('/{id}');
$dm->persist( $route );
$child = new PhpcrRoute();
$child->setPosition( $route, 'bar' );
$dm->persist( $child );
$dm->flush();
With or without default value and requirement only '/foo/bar' and '/foo/*' return matches, but '/foo/1/bar' prompts me with a 'No route found for "GET /foo/1/bar"'.
...
Just now I nearly got it done.
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route as PhpcrRoute;
$dm = $this->get('cmf_routing.route_provider');
$route = new PhpcrRoute();
$route->setPosition( $dm->find( null, '/cms/routes' ), 'example_route' );
$dm->persist( $route );
$route->setPrefix( '/cms/routes/example_route' );
$route->setPath( '/foo/{id}/bar' );
$dm->flush();
If prefix is '/cms/routes' and name is 'foo' everything works fine. But now that I got this far, assigning a speaking name would round it up.
Thanks in advice!

You got quite close to the solution, actually!
When using PHPCR-ODM, the route document id is its path in the repository. PHPCR stores all content in a tree, so every document needs to be in a specific place in the tree. We then use the prefix to get a URL to match. If the prefix is configured as /cms/routes and the request is for /foo, the router looks in /cms/routes/foo. To allow parameters, you can use setVariablePattern as you correctly assumed. For the use case of /foo/{id}/bar, you need to do setVariablePattern('/{id}/bar'). You could also have setVariablePattern('/{context}/{id}') (this is what the doc paragraph you quoted meant - i will look into adding an example there as its indeed not helpful to say "you can do this" but not explain how to).
Calling setPath is not recommended as its just less explicit - but as you noticed, it would get the job done. See the phpdoc and implementation of Model\Route::setPattern:
/**
* It is recommended to use setVariablePattern to just set the part after
* the static part. If you use this method, it will ensure that the
* static part is not changed and only change the variable part.
*
* When using PHPCR-ODM, make sure to persist the route before calling this
* to have the id field initialized.
*/
public function setPath($pattern)
{
$len = strlen($this->getStaticPrefix());
if (strncmp($this->getStaticPrefix(), $pattern, $len)) {
throw new \InvalidArgumentException('You can not set a pattern for the route that does not start with its current static prefix. First update the static prefix or directly use setVariablePattern.');
}
return $this->setVariablePattern(substr($pattern, $len));
}
About explicit names: The repository path is also the name of the route, in the example /cms/routes/foo. But it is not a good idea to use a route name of a dynamic route in your code, as those routes are supposed to be editable (and deletable) by an admin. If you have a route that exists for sure and is at a specific path, use the configured symfony routes (the routing.yml file). If its dynamic routes, have a look at the CMF Resource Bundle. It allows to define a role for a document and a way to look up documents by role. If you have a route with a specific role that you want to link to from your controller / template, this is the way to go. If you have a content document that is linked with a route document and have that content document available, your third and best option is to generate the URL from the content document. The CMF dynamic router can do that, just use the content object where you normally specify the route name.

Related

How to set a context variable with dot in name?

I am trying to add a context data variable (CDV), which has a dot in its name. According to Adobe site this is correct:
s.contextData['myco.rsid'] = 'value'
Unfortunately, after calling s.t() the variable is split into two or more:
Context Variables
myco.:
rsid: value
.myco:
How can I set the variable and prevent splitting it into pieces?
You are setting it properly already. If you are referring to what you see in the request URL, that's how the Adobe library sends it. In your example, "myco" is a namespace, and "rsid" is a variable in that namespace. And you can have other variables in that namespace. For example if you have
s.contextData['myco.rsid1'] = 'value';
s.contextData['myco.rsid2'] = 'value';
You would see in the AA request URL (just showing the relevant part):
c.&myco.&rsid1=value&rsid2=value&.myco&.c
I assume you are asking because you want to more easily parse/qa AA collection request URLs from the browser network tab, extension, or some unit tester? There is no way to force AA to not behave like this when using dot syntax (namespaces) in your variables.
But, there isn't anything particularly special about using namespaces for your contextData variables; it's just there for your own organization if you choose. So if you want all variables to be "top level" and show full names in the request URL, then do not use dot syntax.
If you want to still have some measure of organization/hierarchy, I suggest you instead use an underscore _ :
s.contextData['myco_rsid1'] = 'value';
s.contextData['myco_rsid2'] = 'value';
Which will give you:
c.&myco_rsid1=value&myco_rsid2=value&.c
Side Note: You cannot do full object/dot notation syntax with s.contextData, e.g.
s.contextData = {
foo:'bar', // <--- this will properly parse
myco:{ // this will not properly parse
rsid:'value' //
} //
};
AA library does not parse this correctly; it just loops through top level properties of contextData when building the request URL. So if you do full object syntax like above, you will end up with:
c.&foo=bar&myco=%5Bobject%20Object%5D&&.c
foo would be okay, but you end up with just myco with "[object Object]" as the recorded value. Why Adobe didn't allow for full object syntax and just JSON.stringify(s.contextData) ? ¯\_(ツ)_/¯

ControlPath equivalent from DotNetNuke DnnApiController ActiveModule

Is there a way to get the module root folder (folder under DesktopModules) of the ActiveModule from a DnnApiController?
In PortalModuleBase I would use the ControlPath property to get to the same root folder I'm looking for.
As #MitchelSellers points out, it doesn't appear to be in the API so you have to figure it out yourself.
Since the API gives us the ActiveModule which is a ModuleInfo that's probably the best way to get at it.
If your modules use a pretty standard consistent naming then the following "best guess" method should work pretty well
public static string ControlPath(ModuleInfo mi, bool isMvc = false)
{
return isMvc
? $"/DesktopModules/MVC/{mi.DesktopModule.FolderName}"
: $"/DesktopModules/{mi.DesktopModule.FolderName}";
}
The other way is to look at the ModuleDefinitions of our module and grab the first ModuleControl and look at it's ControlSrc to see it's path.
public static string ControlPath(ModuleInfo mi)
{
var mdi = mi.DesktopModule.ModuleDefinitions.First().Value;
var mci = mdi.ModuleControls.First().Value; // 1st ModuleControl
return Path.GetDirectoryName(mci.ControlSrc);
}
The second method is really messy (and untested) but should give you the actual folder path where the controls are installed, over the other best guess method above.
From the API's it doesn't appear so, you should know the path for this though since you are inside of your module, the only concern is if you are inside of a child portal you need the prefix, which you should be able to get. I'd just use Server.ResolveClientUrl() to get it.

cakePHP get a variable from each Model and Controller

I got a question. I have a db table with settings (id, name).
If I read them from the db
$settings = $this->Setting->find('list');
How can I do this in the AppController or something like that to access from each Controller and Model?
Hope someone can help me.
Thanks
Explanation:
I would assume you're looking for something like below (Obviously you'll want to tweak it per your own application, but - it's the idea).
In the app controller, it
finds the settings from the table
repeats through each and puts each one into a "Configure" variable
Code:
/**
* Read settings from DB and populate them in constants
*/
function fetchSettings(){
$this->loadModel('Setting');
$settings = $this->Setting->findAll();
foreach($settings as $settingsData) {
$value = $settingsData['Setting']['default_value'];
//note: can't check for !empty because some values are 0 (zero)
if(isset($settingsData['Setting']['value'])
&& $settingsData['Setting']['value'] !== null
&& $settingsData['Setting']['value'] !== '') {
$value = $settingsData['Setting']['value'];
}
Configure::write($settingsData['Setting']['key'], $value);
}
}
Then, you can access them anywhere in your app via Configure::read('myVar');
A warning from the CakePHP book about Configure variables. (I think they're fine to use in this case, but - something to keep in mind):
CakePHP’s Configure class can be used to store and retrieve
application or runtime specific values. Be careful, this class allows
you to store anything in it, then use it in any other part of your
code: a sure temptation to break the MVC pattern CakePHP was designed
for. The main goal of Configure class is to keep centralized variables
that can be shared between many objects. Remember to try to live by
“convention over configuration” and you won’t end up breaking the MVC
structure we’ve set in place.

Trouble using dbforge with PyroCMS (CI based CMS)

I have been using PyroCMS and CI for quite some time, and truly love it.
I am extending a DB module that will allow an admin user to manage a DB without having to use something like phpMyAdmin.
The only thing I have been able to get working however is Browsing a table's field values (i.e 'SELECT * FROM 'table_name').
I want to include more functions, but I can't seem to get dbforge to work properly. I know it is loaded because dbforge is used to uninstall modules. I also get no error when calling functions from it.
Here is an example of my code from the controller (dbforge has already been loaded).
public function drop($table_name)
{
$table_name = $this->uri->segment(4);
$this->dbforge->drop_table($table_name);
redirect('admin/database/tables');
}
Lets say the function gets called from this url:
.../admin/database/drop/table_name
It appears to work... but instead it just redirects to the tables overview.
Is there something I am missing? Shouldn't [$this->dbforge->drop_table($table_name);] always drop a table (given $table_name is valid)?
EDIT
As a work around, I was able to use:
public function drop($table_name)
{
$table_name = $this->uri->segment(4);
//$this->dbforge->drop_table($table_name);
$this->db->query("DROP TABLE ".$table_name);
redirect('admin/database/tables');
return TRUE;
}
I really would like to use DB forge, however...
I think you might be getting a little confused by the site prefixes in PyroCMS 1.3.x.
By default all installations of Community and Professional will have default_ as a prefix for all tables in the first site. If you have Professional you can add new sites and the site reference will be whatever_ instead of default_
This prefix is accounted for by dbforge, so when you want to delete default_blog you would just delete:
/admin/database/drop/blog
Also, why are you accepting the $table_name as an argument then overriding it with a uri segment?
Also, why are you accepting the $table_name as an argument then overriding it with a uri segment?
See what I did there? xD
public function drop($table_name)
{
$this->dbforge->drop_table($table_name);
redirect('admin/database/tables');
}

how to force drupal function to not use DB cache?

i have a module and i am using node_load(array('nid' => arg(1)));
now the problem is that this function keep getting its data for node_load from DB cache.
how can i force this function to not use DB cache?
Example
my link is http://mydomain.com/node/344983
now:
$node=node_load(array('nid'=>arg(1)),null,true);
echo $node->nid . " -- " arg(1);
output
435632 -- 435632
which is a randomly node id (available on the system)
and everytime i ctrl+F5 my browser i get new nid!!
Thanks for your help
Where are you calling this? For example, are you using it as part of your template.php file, as part of a page, or as an external module?
Unless you have this wrapped in a function with its own namespace, try naming the variable differently than $node -- for example, name it $my_node. Depending on the context, the 'node' name is very likely to be accessed and modified by Drupal core and other modules.
If this is happening inside of a function, try the following and let me know what the output is:
$test_node_1 = node_load(344983); // Any hard-coded $nid that actually exists
echo $test_node_1->nid;
$test_node_2 = node_load(arg(1)); // Consider using hook_menu loaders instead of arg() in the future, but that's another discussion
echo $test_node_2->nid;
$test_node_3 = menu_get_object(); // Another method that is better than arg()
echo $test_node_3->nid;
Edit:
Since you're using hook_block, I think I see your problem -- the block itself is being cached, not the node.
Try setting BLOCK_NO_CACHE or BLOCK_CACHE_PER_PAGE in hook_block, per the documentation at http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_block/6
You should also try to avoid arg() whenever possible -- it's a little bit of a security risk, and there are better ways to accomplish just about anything arg() would do in a module environment.
Edit:*
Some sample code that shows what I'm referring to:
function foo_block ($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0] = array(
'info' => 'I am a block!',
'status' => 1,
'cache' => BLOCK_NO_CACHE // Add this line
);
return $block;
case 'view':
.....
}
}
node_load uses db_query, which uses mysql_query -- so there's no way to easily change the database's cache through that function.
But, node_load does use Drupal's static $nodes cache -- It's possible that this is your problem instead of the database's cache. You can have node_load clear that cache by calling node_load with $reset = TRUE (node_load($nid, NULL, TRUE).
Full documentation is on the node_load manual page at http://api.drupal.org/api/drupal/modules--node--node.module/function/node_load/6
I have had luck passing in the node id to node_load not in an array.
node_load(1);
According to Druapl's api this is acceptable and it looks like if you pass in an array as the first variable it's loaded as an array of conditions to match against in the database query.
The issue is not with arg(), your issue is that you have caching enabled for anonymous users.
You can switch off caching, or you can exclude your module's menu items from the cache with the cache exclude module.
edit: As you've now explained that this is a block, you can use BLOCK_NO_CACHE in hook_block to exclude your block from the block cache.

Resources