Consider a view called render_thing, which I load from a controller like so:
$html = $this->load->view(
'render_thing',
array(
'someParam' => $globalParam
'permissionMode' => 'guest'
),
true
);
log($html);
Later on in that same controller, I load the view again, except I don't override the optional permissionMode parameter. I'm assume that in the view code, $permissionMode would be unset.
$moreHtml = $this->load->view(
'render_thing',
array(
'someParam' => 'blablabla'
),
true
);
However, in the render_thing view code, on the second call, $permissionMode is still 'guest'. Can you tell me what is going on here?
Thanks!!!
From Loader.php, Loader::_ci_load in the CodeIgniter source...
/*
* Extract and cache variables
*
* You can either set variables using the dedicated $this->load_vars()
* function or via the second parameter of this function. We'll merge
* the two types and cache them so that views that are embedded within
* other views can have access to these variables.
*/
if (is_array($_ci_vars))
{
$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
}
extract($this->_ci_cached_vars);
So, this would be why the parameter is still set. load_vars is not a method, but vars is; problem is that it doesn't provide a facility to erase the cache. Therefore, since CodeIgniter is still PHP4 compatible, you may always do this: $this->load->_ci_cached_vars = array();.
I've had the same problem and figured out the problem in Loader.php as follows;
/*
* Extract and cache variables
*
* You can either set variables using the dedicated $this->load_vars()
* function or via the second parameter of this function. We'll merge
* the two types and cache them so that views that are embedded within
* other views can have access to these variables.
*/
if (is_array($_ci_vars))
{
$this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
}else{
$this->load->_ci_cached_vars = array();
}
extract($this->_ci_cached_vars);
Related
If you have data that you want to be bound to a view each time that view is
rendered,a view composer can help you ...
This task can be easily archived in laravel, but I am now using Codeigniter, there is no view composers things. What I have done now is, I create a custom view method, just like below
public function view($page,$params=null,$return=false)
{
// Every time I invoke this method, $nav will be passed to 'navigation' view.
$nav=[
'user' =>'Adam'
];
//return the views as view_partials instead of displayed
$view_partials = array(
'navigation' => $this->obj->load->view('partials/nav',$nav,true),
'page_content' => $this->obj->load->view($page,$params,true)
);
// load layout with the view_partials which contain bound data.
$this->obj->load->view($this->_layout,$view_partials,$return);
}
This method returns views as 'string', it can not works with json or complex page.... Thank you.
Ok, lets make this simple
$this->load->view(path_to_htmlpage, $bound_data, FALSE);
or
$this->load->view(path_to_htmlpage, $bound_data); // by default 3rd param is FALSE
this will render html page
If you want to get html page as a string, set 3rd parameter to TRUE
$html_string = $this->load->view(path_to_htmlpage, $bound_data, TRUE);
check https://ellislab.com/codeigniter/user-guide/general/views.html
public function view($page,$params=null,$return=false)
{
$nav['user']='adam';
$data['navigation']= $this->load->view("partials/nav",$nav,true);
$data['page_content'] = $this->load->view($page,$params,true)
$this->load->view('your_page',$data);
}
in yor view page ie.your_page you can use variables $navigation and $page_content to display the pages
The goal is to activate an existent topic in a Blog Entry page. Normally a user does this in the Pages Attributes section like so:
Now my goal is to do this programmaticaly. I won't post all my trials (since 2 days) here because it's just crap, but here's what I've done so far.
First I add a Blog Page to a chosen parent Page (ID 157):
use Concrete\Core\Page;
$parentPage = Page\Page::getByID(157);
$template = \PageTemplate::getByHandle('blog_entry');
$entry = $parentPage->add($type, array(
'cName' => 'My title',
'cDescription' => 'description',
'cHandle' => 'my_title',
'cvIsApproved' => true,
'cDatePublic' => $publishDate->format('Y-m-d H:i:s')
), $template);
As the newly created page is a blog_entry template the Blog Entry Topics is already assigned.
Then I create a Topic and add it to its Topic Tree (Blog Entry Topics) like so:
use \Concrete\Core\Tree\Type\Topic as TopicTree;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
use \Concrete\Core\Tree\Node\Node as TreeNode;
$topicTree = TopicTree::getByName('Blog Entries');
$parentTopic = TreeNode::getByID($topicTree->getRootTreeNodeObject()->treeNodeID);
$item0 = TopicTreeNode::add('udland', $parentTopic);
How to activate/assign this Topic(Udland) to my page ($entry)? (As shown in the image)
I know it must be related to the DB-tables CollectionAttributeValues and atSelectedTopics. Also the Classes CollectionValue and CollectionKey must be involved.
I could add those entries manually in the DB but this isn't a good idea because I don't know what data is necessary to make this work correctly. The topics are used to filter Blog entries so I'm quite sure that there are other tables involved and as a Core developer said: "These are fragile little things" ;-).
As this version of concrete5 is a complete new launch, the developer docs aren't complete and after 2 days of digging inside the core code I'm just desperate.
Update (after a week of digging...)
I managed to do a hack taken out of a Controller method: (/concrete/controllers/panel/page/attributes.php -> submit()).
I know this isn't the way to go at all but it's my best trial so far:
(I just include the NameSpaces here to make clear what Classes I'm calling)
use Concrete\Core\Page;
use Concrete\Core\Page\Collection\Version\Version;
use Concrete\Core\Workflow\Request\ApprovePageRequest;
use CollectionAttributeKey;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
Get the Attributes ID by handle:
$ak = CollectionAttributeKey::getByHandle('blog_entry_topics');
$attributekID = $ak->getAttributeKeyID();
get the topic
$item_one = TopicTreeNode::getNodeByName('Udland');
then simulate a posted form by:
$_POST = array(
'topics_' . $attributekID => array($item_one->treeNodeID)
);
I know this is so ugly and a big hack & not reliable at all but as said it's taken out of a Controller...
Then I do a slimmed version of the submit() method:
$c = Page\Page::getByID(157);
$published = new \DateTime();
$nvc = $c->getVersionToModify();
$nvcObj = $nvc->getVersionObject();
$data = array();
$data['cName'] = $nvcObj->cvName;
$data['cDescription'] = $nvcObj->cvDescription;
$data['cDatePublic'] = $published->format('Y-m-d H:i:s');
$data['uID'] = '1';
$nvc->update($data);
$setAttribs = $nvc->getSetCollectionAttributes();
$processedAttributes = array();
$selectedAKIDs = $attributekID;
if (!is_array($selectedAKIDs)) {
$selectedAKIDs = array();
}
$selected = is_array(array($attributekID)) ? array($attributekID) : array();
foreach ($setAttribs as $ak) {
if (in_array($ak->getAttributeKeyID(), $selected)) {
$ak->saveAttributeForm($nvc);
} else {
$nvc->clearAttribute($ak);
}
$processedAttributes[] = $ak->getAttributeKeyID();
}
$newAttributes = array_diff($selectedAKIDs, $processedAttributes);
foreach ($newAttributes as $akID) {
$ak = CollectionAttributeKey::getByID($akID);
$ak->saveAttributeForm($nvc);
}
So as said before this is really ugly but it's the best trial so far and somehow it works.
Then approve the Request by doing:
$pkr = new ApprovePageRequest();
$u = new User();
$pkr->setRequestedPage($c);
$v = Version::get($c, "RECENT");
$pkr->setRequestedVersionID($v->getVersionID());
$pkr->setRequesterUserID($u->getUserID());
$pkr->trigger();
$u->unloadCollectionEdit();
But what really makes me wonder is that method inside of /concrete/src/Attribute/Key/Key.php where finally the thing should happen (in my humble opinion):
/**
* Calls the functions necessary to save this attribute to the database. If no passed value is passed, then we save it via the stock form.
* NOTE: this code is screwy because all code ever written that EXTENDS this code creates an attribute value object and passes it in, like
* this code implies. But if you call this code directly it passes the object that you're messing with (Page, User, etc...) in as the $attributeValue
* object, which is obviously not right. So we're going to do a little procedural if/then checks in this to ensure we're passing the right
* stuff
*
* #param CollectionValue|mixed $mixed
* #param mixed $passedValue
*/
protected function saveAttribute($mixed, $passedValue = false)
{
/** #var \Concrete\Core\Attribute\Type $at */
$at = $this->getAttributeType();
$at->getController()->setAttributeKey($this);
if ($mixed instanceof AttributeValue) {
$attributeValue = $mixed;
} else {
// $mixed is ACTUALLY the object that we're setting the attribute against
//todo: figure out what $nvc should really be since it doesn't exist in this scope
$attributeValue = $nvc->getAttributeValueObject($mixed, true);
}
$at->getController()->setAttributeValue($attributeValue);
if ($passedValue) {
$at->getController()->saveValue($passedValue);
} else {
$at->getController()->saveForm($at->getController()->post());
}
$at->__destruct();
unset($at);
}
So I'm really curios to see what the reliable and system-suitable way is to resolve this.
Here's what I came up with that does work. You were pretty close.
use \Concrete\Core\Tree\Type\Topic as TopicTree;
use \Concrete\Core\Tree\Node\Type\Topic as TopicTreeNode;
use \Concrete\Core\Tree\Node\Node as TreeNode;
$parentPage = \Page::getbyPath('/blog');
$template = \PageTemplate::getByHandle('blog_entry');
$entry = $parentPage->add($type, array(
'cName' => 'ooops',
'cDescription' => 'hmmmm',
'cHandle' => 'yay',
'cvIsApproved' => true,
'cDatePublic' => '2015-12-21 00:00:00'
), $template);
$item0 = TopicTreeNode::getNodeByName('udland');
if (!$item0) {
$topicTree = TopicTree::getByName('Blog Entries');
$parentTopic = TreeNode::getByID($topicTree->getRootTreeNodeObject()->treeNodeID);
$item0 = TopicTreeNode::add('udland', $parentTopic);
}
$entry->setAttribute('blog_entry_topics', array($item0->getTreeNodeDisplayPath()));
It looks like the attribute takes in an array of node display paths and that is how it sets the selection. Additionally, you have to use the \Page alias, and not the fully qualified namespace as you were doing, otherwise you get an error about it being unable to clear the cache.
I’m trying to process a menu in the controller as follows:
/**
* Renders the html output for the navigation items
*/
function _render_nav_items($current, $base_url) {
$nav_items = '';
foreach($this->_get_nav_items() as $nav_item) {
$nav_items .= $this->load->view(
'admin/nav/nav_item',
array(
'nav_item' => $nav_item,
'base_url' => $base_url
),
true
);
}
return $nav_items;
}
nav_item.php :
<li><?=$nav_item['name']?></li>
But I get the following output:
http://i.imgur.com/YMJ5kYU.png?2
Any ideas of why that is, I’ve tried running trim and compress on the output but I can’t get those extra characters/whitespace removed from the output.
I have ignored this issue for some time but can't seem to solve it however it seems to occur wherever I have php-code in the view I load, when the php code is removed it loads fine with no extra spaces/characters.
On laravel 4 I could generate a url with query strings using the route() helper. But on 4.1 instead of:
$url = url('admin.events', array('lang' => 'en'));
// admineventsurl/?lang=en
I get:
$url = url('admin.events', array('lang' => 'en'));
// admineventsurl/en
I did some research and all laravel methods to generate url are using the parameters like that. How can I generate the url with query strings?
Laravel's route() and action() helper methods support URL query params. The url() helper method, unfortunately does not.
Simply provide an array with key => value pairs to the route parameters. For example:
route('products.index', ['manufacturer' => 'Samsung']);
// Returns 'http://localhost/products?manufacturer=Samsung'
You can also still include your route parameters (such as ID's and models) to accompany these parameters:
route('products.show', [$product->id, 'model' => 'T9X']);
// Returns 'http://localhost/products/1?model=T9X'
Basically, any elements in the array that contain string keys will be treated as query parameter (/products?param=value). Anything with an integer array key will be treated as a URL argument (/products/{arg}).
This is also supported in action methods:
action('ProductController#index', ['manufacturer' => 'Samsung']);
You can also supply query parameters inside the link_to_route() and link_to_action() methods:
link_to_route('products.index', 'Products by Samsung', ['model' => 'Samsung');
link_to_action('ProductController#index', 'Products by Samsung', ['model' => 'Samsung']);
2019 - EDIT:
If you can't use route() or action(), you can generate a URL with query params using the Arr::query() helper:
url('/products?').\Illuminate\Support\Arr::query(['manufacturer' => 'Samsung']);
// Returns 'http://localhost/products?manufacturer=Samsung'
Or:
url('/products?').http_build_query(['manufacturer' => 'Samsung'], null, '&', PHP_QUERY_RFC3986);
// Returns 'http://localhost/products?manufacturer=Samsung'
Or create a simple helper function:
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
function url_query($to, array $params = [], array $additional = []) {
return Str::finish(url($to, $additional), '?') . Arr::query($params);
}
Then call it:
url_query('products', ['manufacturer' => 'Samsung']);
// Returns 'http://localhost/products?manufacturer=Samsung'
url_query('products', ['manufacturer' => 'Samsung'], [$product->id]);
// Returns 'http://localhost/products/1?manufacturer=Samsung'
Side note.
I disagree with #Steve Bauman's idea (in his answer) that one rarely needs querystring urls, and think that Laravel should at least consider adding querystring functionality (back) in. There are plenty of cases when you want a querystring url rather than a param based "pretty url". For example, a complex search filter...
example.com/search/red/large/rabid/female/bunny
...may potentially refer to the same exact set of rodents as...
example.com/search/bunny/rabid/large/female/red
...but any way you look at it (programming, marketing analytics, SEO, user-friendliness), it's kinda terrible. Even though...
example.com/search?critter=bunny&gender=female&temperament=rabid&size=large&color=red
...is longer and "uglier", it actually is better in this not-so-rare case. Net: Friendly URLs are great for some things, querystrings are great for others.
Answer to the original question...
I needed a "querystring" version of url() -- so I copied the function, modified it, and stuck it in /app/start/global.php:
/**
* Generate a querystring url for the application.
*
* Assumes that you want a URL with a querystring rather than route params
* (which is what the default url() helper does)
*
* #param string $path
* #param mixed $qs
* #param bool $secure
* #return string
*/
function qs_url($path = null, $qs = array(), $secure = null)
{
$url = app('url')->to($path, $secure);
if (count($qs)){
foreach($qs as $key => $value){
$qs[$key] = sprintf('%s=%s',$key, urlencode($value));
}
$url = sprintf('%s?%s', $url, implode('&', $qs));
}
return $url;
}
Example:
$url = qs_url('sign-in', array('email'=>$user->email));
//http://example.loc/sign-in?email=chris%40foobar.com
Note: It appears that the url() function is pluggable, that is, you can replace it. Look in vendor/laravel/framework/src/Illuminate/Support/helpers.php: the url function is wrapped in a if ( ! function_exists('url')) conditional. But you would probably have to jump through hoops to do it (i.e. have laravel load it before its version.)
Cheers,
Chris
The following was what I needed to do:
I handle all of my routing in a service provider, where I had defined the following function:
private function registerRestfulController($prefix, $controllerClass)
{
Route::controller($prefix, $controllerClass, $controllerClass::getRouteNames());
}
getRouteNames is a static method on my BaseController that conventionally returns routes so that RESTful controllers can have automatic named routes.
The problem I was running into was that this defined the set of wildcard matchers on the route itself - in order to avoid that, I add the following to the private function above:
foreach ($controllerClass::getRoutesNames() as $name) {
$route = Route::getRoutes()->getByName($name);
$cleanUri = preg_replace('/\/\{\w*\?\}/', '', $route->getUri());
$route->setUri($cleanUri);
}
This loads all the routes you are registering at the time and immediately removes wildcards from the URI. You could easily pass a boolean or "white-list" of route names that you want to preserve wildcards for, so that it doesn't stomp all over the Laravel default without the intention. Once you run this, it automatically starts working with query string variables, which I find far preferable to path variables in this instance.
A simple way to do this, specially to use with jQuery Autocomplete, it's modify the Controller with a condition to check if has 'term' in the $request:
(Controller file)
public function list_for_autocomplete(Request $request)
{
if ($request->has('term')) {
return YourModel::select('column_name as value')
->where('column_name', 'like', '%' . $request->input('term') . '%')
->get()
}
}
I have project-range meta tags that are need to be set.
I've put them in protected method _initMeta in Bootstrap class.
Are there any better options? What if I would like different set of this data for another languages?
protected function _initMeta(){
$this->bootstrap('view');
$view = $this->getResource('view');
$view->doctype('XHTML1_STRICT');
$view->headTitle()->headTitle('Foo title');
$view->headMeta()->appendName('keywords','foo');
$view->headMeta()->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8')
->appendHttpEquiv('Content-Language', 'any');
$view->headLink()->appendStylesheet('/foo.css')->headLink(array('rel' => 'favicon',
'href' => '/favicon.ico'),
'PREPEND');
}
I use config for basic (bootstrap) data as:
application.ini
resources.view.meta.name.Viewport = "width=device-width, initial-scale=1.0"
resources.view.meta.name.MobileOptimized = "width"
resources.view.meta.name.HandheldFriendly = "true"
resources.view.meta.name.Keywords = "basic,keywords"
...
; format resources.view.headStyle.{MEDIA}.nfile =
resources.view.headStyle.all.1.href = "/css/basic.css"
resources.view.headStyle.all.1.conditionalStylesheet =
resources.view.headStyle.all.1.extras.title = "Basic style"
resources.view.headStyle.all.1.extras.charset = "utf-8"
resources.view.headStyle.all.2.href = "/css/ie.css"
resources.view.headStyle.all.2.conditionalStylesheet = "IE"
resources.view.headStyle.all.2.extras.title = "Internet Explorer style"
resources.view.headStyle.all.2.extras.charset = "utf-8"
; print media example
resources.view.headStyle.print.1.href = "/css/print.css"
...
; format resources.view.headLink.{REL} =
resources.view.headLink.humans.href = "/humans.txt"
resources.view.headLink.humans.type = "text/plain"
; ___ will be replaced by space, __ by point (or set another nest separator)
resources.view.headLink.shortcut___icon.href = "/favicon.png"
resources.view.headLink.shortcut___icon.type = "image/png"
...
At this point, maybe you have some special data. For example in:
project1.ini
project.headLink.author.href = "https://plus.google.com/XXXXX?rel=author"
project.headLink.image_src.href = "/author.jpg"
project.headLink.image_src.type = "image/jpg"
And finally, you mix all in your
Bootstrap.php
(example for *_initHeadLink()*):
// $options = your app options (basic)
// $projectOptions = your project options (special)
// $assets_url = your assets url
if ( is_array($headStyle = $options['headStyle']) ) {
foreach ( $headStyle as $media => $value ) {
foreach ( $value as $style ) {
extract($style);
$this->view->headLink()->appendStylesheet($assets_url . $href, $media,
$conditionalStylesheet, $extras);
}
}
}
$headLinks = array();
if ( isset($options['headLink']) )
$headLinks = $options['headLink'];
if ( isset($projectOptions['headLink']) )
$headLinks = array_merge($headLinks, (array) $projectOptions['headLink']);
// *array key, is the value for rel
foreach ( $headLinks as $rel => $value ) {
$rel = str_replace(array('___', '__'), array(' ', '.'), $rel);
$this->view->headLink()->headLink(array_merge(array('rel' => $rel), (array) $value));
}
Then, you can override these data from your Controller: setName, set...
I hope it helps ;)
You have several ways of achieving this. First and foremost, make sure the moment you define the metadata you already know what language will be loaded for the current request. Sometimes this may not be easy to determine at bootstrap time.
Having said this, besides the bootstrap, you can set the metadata on a:
Front Controller Plugin class (using for example the dispatchLoopStartup() or predispatch() methods).
Action Helper class (using for example the init() or preDispatch() methods).
At those points of execution you probably already determined the language to use, and can set the metadata accordingly. You can always change the metadata afterwards in your action controllers for specific cases in your application, so you're never really stuck if you previously specified metadata.
In my own work, I have this setup:
Front Controller Plugin, dispatchLoopStartup() method: determine language to load, giving priority to a "lang" GET parameter in the request object, then browser language, then default site language. I also use this to determine if the request is a normal request or an ajax request; if it's the former case, I register an action helper, see next...
Action Helper, preDispatch() method: load metadata depending on language and other stuff, load Layout and widgets, etc.
Action controller, someAction() method: if necessary, change some of the previously set metadata, for example headTitle(), which can depend on the effectively loaded content.
This arrangement makes sense to me, maybe it can help your approach?
The bootstrap is way to early for my projects. I add them in my controller/actions
$keywords = 'php,zend,framework';
$this->view->headMeta($keywords,'keywords','name',array(),'SET');
... etc.
Actually very late almost at the end. At this point I would also know about language and other things.