Passing data from view to Layout in Codeigniter 4 - php

I'm brand new to CodeIgniter, so apologies if I'm missing something obvious here.
I'm comfortable with sending data from a controller to a view file using return view('default/blog/index', $data);. My issue is accessing the same data in a layout file which is extended by the view file using <?= $this->extend('layouts/default'); ?>.
For example, if I insert <?= $data['content'] ?> in my view file, it displays as expected. If I insert the same code in the layout file that is extended by my view file, I get the "Trying to access array offset on value of type null" exception.
What am I missing that will allow me to access my data from within the layout file?
Thanks in advance.
Update:
So in my BlogController I've got
class BlogController extends BaseController
{
public function index()
{
$model = new Blog();
$data = $model->getBlog();
return view('default/blog/index', ['data' => $data]);
}
public function item($slug = null){
$model = new Blog();
$data = $model->getBlog($slug);
return view('default/blog/item', ['data' => $data]);
}
}
And then in my item.php and index.php files, I have
<?= $this->extend('layouts/default', ['data' => $data]); ?>
My Blog Model's getBlog() method:
public function getBlog($slug = false)
{
if ($slug === false) {
return $this->orderBy('bs_created_dt', 'desc')->findAll();
}
return $this->where(['bs_slug' => $slug])->first();
}
When I use the debug toolbar to inspect the data, it is showing as expected, and I can display it in the view files, but not in the layout file.

In Codeigniter, you need to pass data also in an extended file called layouts.
Because you want to access data inside the extended file and for that, you just need to pass data to that file.
So replace the line of code of extended view with this :
$this->extend('layouts/default', ['data' => $data]);

Figured this out - absolute rookie mistake.
I'm working off of a pre-existing template, and the previous developer had overwritten the $data variable in layout file before where I was trying to use it.
I'm off to stand in the corner for a while.

Related

Yii, Is it good MVC practice to echo in the controller?

It seems like i am not following the MVC design structure.
I'm making an ajax call from my view to a Controller function
Controller
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array('email'=>$_POST['email'], 'password'=>$_POST['pass']));
echo $user->fullname;
}
View (the calling ajax)
CHtml::ajaxLink(
$text = 'get user',
$url = Yii::app()->createUrl('[my controller]/getClient'),
$ajax=array (
'type'=>'POST',
'data' => array('email'=>email, 'pass'=>pass),
'beforeSend' => "function( request )
{
$(\".result\").html(\"fetching...\")
}",
'success'=>"function(data){
$(\".result\").html(\"user is :\"+data)
}
"
));
Is it good to "echo" the $user->fullname inside the controller for the ajax success function to display it? My boss doesn't like it when i print stuff in my controller, how can i approach this
because when i use return instead, the ajax success gets a null value
return $user->fullname;
No,
It's not a good practice.
You need to create a view to use echo.
You can use return $this->renderPartial('VIEW_NAME'); to render a view without Layout.
You should write 'return' instead of 'echo'. 'echo' is not a good practice for ajax response. You don't need to make a new view for just return a name in your case.
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array('email'=>$_POST['email'],'password'=>$_POST['pass']));
return $user->fullname;
}
No. A controller’s supposed to pass its results to a view for rendering.
I would avoid echoing in the controller what we usually do is have a ajax view folder and a json view and render with that so:
public function actionGetClient()
{
$user = Client::model()->findByAttributes(array(
'email'=>$_POST['email'],
'password'=>$_POST['pass']
));
$this->render("json",array("outputData"=>$user));
}
then add this to the controller as well:
public function getViewPath(){
if(Yii::app()->request->isAjaxRequest){
if(($module=$this->getModule())===null)
$module=Yii::app();
return $module->getViewPath().DIRECTORY_SEPARATOR."ajax";
}
return parent::getViewPath();
}
and in the ajax views folder add a json.php file like so
header('Content-Type: application/json');
// output data
echo json_encode($outputData);
please degug the code as I wrote it free hand. You can also set a marker in the controller like $viewPath and set it before the rendering

how to use forward in view zend 2

I want to write a plugin in ZF2,
An example of the plugin is a like button that shows in every post. It should for example print in PostsAction,
I know I can use:
$like = $this->forward()->dispatch('Application\Controller\Index', array(
'action' => 'like',
'postId' => $Id
));
$like variable returns a button that users can click on.
But I want to echo this in the view. In forward the view is not defined.
Also if I use
return $this->getView()->render('application/index/like', array('postId' => $Id));
I don't have access to postId in likeController, because it is set in the view. How I can implement these type of plugins that need a dynamic variables?
Looks like you only need partials. A partial in ZF2 is only a view which you print in another view and give some params to it.
So you could define a View:
// application/partials/button.phtml
<button data-postId="<?php echo $this->postId ?>">Like It!</button>
And use it in other View:
echo $this->partial('application/partials/button.phtml', array(
'postId' => $thePostId
));
Official Documentation
Nice Answer on SO to implement with template_map
Solution using view helper
I think what you are looking for is a custom view helper. You can read on this in the official ZF2 documentation.
You have to write your custom button view helper, register it and then you can use it in your view.
The helper class:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LikeButtonHelper extends AbstractHelper
{
public function __invoke($post)
{
//return here your button logic, you will have access to $post
}
}
Register your helper within a configuration file:
'view_helpers' => array(
'invokables' => array(
'likeButtonHelper' => 'Application\View\Helper\LikeButtonHelper',
),
)
And finally in the view you can use it like this:
foreach($posts as $post){
echo( ... your code to show the post ...);
echo $this->likeButtonHelper($post);
}
UPDATE - Solution using forward plugin
I think I get what you mean now. I also think the example you are talking about is what in the ZF2 forward plugin documentation is referred to as “widgetized” content.
I think you are doing it correctly. You can attach the return value $like as a child to the view of the original controller (from where you forwarded in the first place).
So in your WidgetController:
use Zend\View\Model\ViewModel;
class WidgetController extends AbstractActionController
{
public function likeAction()
{
$post= $this->params()->fromRoute('post');
$viewModel = new ViewModel(array('post' => $post));
$viewModel->setTemplate('view/widgets/like');
return $viewModel;
}
}
So in your PostController:
use Zend\View\Model\ViewModel;
class PostController extends AbstractActionController
{
public function postsAction()
{
$likeWidget = $this->forward()->dispatch('Application\Controller\WidgetController', array(
'action' => 'like',
'post' => $post
));
$viewModel = new ViewModel();
$viewModel->setTemplate('view/posts/post');
$viewModel = new ViewModel(array(
//...add your other view variables...
));
// Add the result from the forward plugin as child to the view model
if ($likeWidget instanceof ViewModel)
{
$viewModel->addChild($likeWidget , 'likeWidget');
}
return $view;
}
}
And finally in your post view template add:
echo($this->likeWidget);
That is where the widget will eventually output.
The problem remains that you can not do this inside a foreach loop (a loop for printing your posts) in the view. That is why I suggested using a view helper and #copynpaste suggests using a partial, those are more suitable for adding additional logic inside a view.
Note:
Personally I don't like this forward solution for something so simple as a like button. There is hardly any logic in the controller and it seems overly complicated. This is more suitable for reusing a whole view/page that will be both rendered by itself as well as nested in another view.
The partials or view helpers seem much more suitable for what you want to do and those are very proper ZF2 solutions.
I found it ,developed by Mohammad Rostami,Special thanks to him :
Plugin In ZF2

Telling cakePHP to render as XML / use xml/blah.ctp instead of blah.ctp

I have been successfully using XML view files in CakePHP (request the XML output type in headers so CakePHP will use e.g. Orders/xml/create.ctp instead of Order/create.ctp).
However, now i need to add some functionality that requires me to the reformat the XML at the end of most business logic in the controller.
So i tried this in the controller action:
public function createorder() {
$this->autoRender = false; // disable automatic content output
$view = new View($this, false); // setup a new view
{ ... all kinds of controller logic ...}
{ ... usually i would be done here and the XML would be outputted, but the autorender will stop that from happening ... }
{ ... now i want the XML in a string so i can manipulate the xml ... }
$view_output = $view->render('createorder'); // something like this
}
But what this gives me is:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<error>View file &quot;/Users/test/Documents/hosts/mycakeapp/app/View/Orders/createorder.ctp&quot; is missing.</error>
<name>MissingViewException</name>
<code>500</code>
<url>/orders/createorder/</url>
</response>
So i need to tell CakePHP to pickup the xml/createorder.ctp instead of createorder.ctp. How do i do this?
Cheers!
This answers refers to cakephp 2.4
I have been successfully using XML view files in CakePHP (request the XML output
type in headers so CakePHP will use e.g. Orders/xml/create.ctp
instead of Order/create.ctp).
In lib/Cake/View you can see different View files like:
View.php
XmlView.php //This extends View.php
JsonView.php //This extends View.php
So you told cakephp to use the XmlView. When you create a new View you need to use the XmlView instead of View. Or you can create your own custom View and put it inside app/View folder. In your custom View you can set your subdir.
<?php
App::uses('View', 'View');
class CustomView extends View {
public $subDir = 'xml';
public function __construct(Controller $controller = null) {
parent::__construct($controller);
}
public function render($view = null, $layout = null) {
return parent::render($view, $layout);
}
}
So what you need now is to create your custom view $view = new CustomView($this, false);
You can also write in your CustomView functions to handle the data as xml and use it to every action.
Also #Jelle Keizer answer should work. $this->render('/xml/createorder'); points to app/View/xml/createorder. If you need this to point to app/View/Order/xml/create just use $this->render('/Orders/xml/create');.
$this->render('/xml/createorder');

Cakephp calling function from view

I have the following function:
public function make_order($id = null){
if($this->request->is('post')){
if(isset($id)){
$single_product = $this->Product->find('first', array('Product.id' => $id));
$this->placeOrder($single_product);
}else{
$product_array = $_SESSION['basket'];
foreach($product_array as $product){
$this->placeOrder($product);
}
}
}
}
private function placeOrder($product){
$order_array = array();
$order_array['Order']['Product_id'] = $product['Product']['id'];
$order_array['Order']['lejer_id'] = $this->userid;
$order_array['Order']['udlejer_id'] = $product['users_id'];
$this->Order->add($order_array);
}
Now these two function are not "connected" to a view but i still need to call them from within another view
For this ive tried the following:
<?php echo $this->Html->link(__('Bestil'), array('action' => 'make_order')); ?>
However this throws an error saying it couldnt find the view matching make_order and for good reason ( i havnt created one and i do not intend to create one)
My question is how do i call and execute this function from within my view?
At the end of your make_order function, you'll either need to:
a) specify a view file to render, or
b) redirect to a different controller and / or action, that does have a view file to render.
a) would look like this:
$this->render('some_other_view_file');
b) might look like this (note: setting the flash message is optional)
$this->Session->setFlash(__('Your order was placed'));
$this->redirect(array('controller' => 'some_controller', 'action' => 'some_action'));
You can turn auto-rendering off by setting $this->autoRender = false; in your controller's action (make_order() in this case). This way you don't need a view file, and you can output whatever you need.
The problem is that nothing will be rendered on the screen. Therefore, my advice is to have your "link" simply call a controller::action via AJAX. If that's not possible in your situation, then you'll have to either render a view in your make_order() method, or redirect to an action that will render a view.

How to load a base template in code igniter

I'm new to working with CI and I have a question. More like a design method suggestion. I want to create a base template for static pages. Basically I want to load doc type, head, and the body but nothing else. I want the content to be loaded by whatever class/function I call via the URL. I know I can do this with a HTML template and str_replace() but is there a better way or some fancy CI method I'm not familiar with?
Here is what I have so far and it works but it's not the best.
class Sandbox extends CI_Controller {
public function __construct() {
parent::__construct();
//echo "hello world?";
}
public function load($what){
if ($what == 'something'){
if (!file_exists('application/views/content/'.$what.'.php')){
// Whoops, we don't have a page for that!
show_404();
}
$data = array();
$this->load->view('templates/header', $data);
$this->load->view('content/'.$what, $data);
$this->load->view('templates/footer', $data);
}
else { // Default load
$data = array();
$data['message'] = 'Hello World';
//$this->load->view('templates/header', $data);
$this->load->view('content/sandbox', $data);
//$this->load->view('templates/footer', $data);
}
}
}
When I load the view method it seems to work but I had to add the doctype and head to the template. I would prefer to load those seperately so do not have to create multiple headers. What I am looking for is a way to specifically load parts of the page and whatever scripts/css that are required for that given page.
Thanks for any advice.
Your function names in the controller are references to the url eg:
www.ci.com/sandbox/load
The above url would load function load() inside the sandbox controller.
To create a template you can just create a view with the basics that you want and then pass the content to that view eg:
function home(){
$data['content'] = $this->load->view('home_content',,TRUE);
this->load->view('template', $data)
}
By using TRUE in the 3rd argument you are returning the data rather than displaying it.
Then in the template file just echo $content;
You could also create separate head files and other files to include in your template using the same method ie:
function home(){
$data['content'] = $this->load->view('home_content',,TRUE);
$data['head'] = $this->load->view('head',,TRUE);
this->load->view('template', $data);
}
You can also pass the data variable to views that you are returning ie:
$data['content'] = $this->load->view('home_content',,TRUE);
$data['admin_script'] = $this->load->view('admin_script',,TRUE);
$data['head'] = $this->load->view('head',$data,TRUE);
I hope this helps! :)
ALSO
If you want to load different pages you should create separate functions for them and create a new routing rule : Codeigniter: URI ROUTING

Categories