I'm creating a mail service within my application that has the body of the email stored in the database prior to sending it out to recipients.
Each mail body is a partial view script that has the necessary parameters injected into it via Zend_View.
What I want to do is create a 'mail' layout that can wrap around each of these partials,
but I can only seem to get either the layout content or the view content; not both at once.
What I've got
$scriptPath = 'test_mail';
$view = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('view');
$view->setScriptPath(APPLICATION_PATH . '/modules/mail/views/scripts/partials/');
$view->layout()->setLayout('mail');
var_dump($view->layout()->render($scriptPath));
However, all I receive is the view script content.
My layout is looking something like this:
<table class="mail">
<!-- Snip -->
<?php echo $this->layout()->content; ?>
<!-- Snip -->
</table>
I know this is possible. I don't want to do:
$layout->content = $view->render($scriptPath);
I assume I'm going the wrong way about this. Is it that I need/ don't have the layout controller plugin registered and somehow need to trigger this to get the output?
I suppose I could just create a custom layout class and take care of the rendering myself but wanted to see what others said first.
Any tips? Thanks!
I am using email layout, multiple view templates for different kinds of emails and extended Zend_Mail class for setting desirable body:
class MyMail extends Zend_Mail
{
public function setBodyView($script, $params = array())
{
$layout = new Zend_Layout(array('layoutPath' => APPLICATION_PATH . '/layouts/scripts'));
$layout->setLayout('email'); // Your email layout
$view = new Zend_View();
$view->setScriptPath(APPLICATION_PATH . PATH_TO_MAIL_TEMPLATES);
foreach ($params as $key => $value) {
$view->assign($key, $value);
}
$layout->content = $view->render($script . '.phtml');
$html = $layout->render();
$this->setBodyHtml($html);
}
}
I using %mail_body% pattern in my mail template.
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$mail_template = $view->render('template.phtml');
$returnYourReadyTemplate = str_replace('%mail_body%', $mail_body, $mail_template);
in template.phtml :
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body >
<div style="margin:30px 20px 10px 20px">
%mail_body%
</div>
</body>
</html>
Hope this helps you!
Related
I made an view called (head.blade.php) and tried to load it in HomeController __construct function with View::make() function. However, the function works, but not with the variables.
For example, here's function, with View::make():
public function __construct() {
$this->asset = new Asset;
$assets = array('core');
$css = $this->asset->generate($assets);
return View::make('includes.head')->with('styles', $css);
}
If I try to use $styles variable in view, it gives me error: (Undefined variable $styles in...-)
But, digging in Laravel docs I have found this method:
public function __construct() {
$this->asset = new Asset;
$assets = array('core');
$css = $this->asset->generate($assets);
View::creator('includes.head', function($view) use ($css) {
$view->with('styles', $css);
});
}
And the method View::creator works.
My question is, how and why the View::make() doesn't work in __construct?
PS. I'm calling the view in another view with #include method.
In general OOP, you do not return any value from a constructor. The implicit return value of a constructor is the object. Remember that the constructor is called whenever a new object is made:
$myObject = new MyObject(); // <-- I just called the MyObject constructor
Instantiation of a Laravel controller happens prior to the dispatching of the route, so returning the View inside the constructor is logically incorrect. See also this answer.
I'm not sure why you're trying to do this, but I believe it could be because you're trying to attach a 'header' view to all of the views returned by this specific controller. If so, this isn't the way you do this in Laravel. To accomplish that, make a layout view that your other views will extend:
<!-- app/views/layout/master.blade.php -->
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<article>
#include('layout.header')
#yield('content')
#include('layout.footer')
</article>
</body>
</html>
<!-- app/views/layout/header.blade.php -->
<header>
A Header
</header>
<!-- app/views/layout/footer.blade.php -->
<footer>
A Footer
</footer>
<!-- app/views/some-view.blade.php -->
#extends('layout.master')
#section('content')
View Content
#stop
With this setup, some-view.blade.php will have both a header and a footer sandwiching the view's main content.
EDIT After staring at the documentation for another hour I think I have finally realized what I need to adjust and how. I'll update this question tomorrow(*EDIT2*the 25th, have to attend to my family today. But I already have most of the functions working ;), most likely with a full solution to all my problems ;)
I know I am asking for a lot, but I figured it would be better to keep everything in one place rather than starting multiple questions. I am trying to implement this tutorial into my CI project. Everything was fine up until the mail confirmation part. In the tutorial they use Swift Mailer, but I would like to stick to the CI's mail helper.
The problem is that the code is somewhat complicated, due to the use of templates and the confirmation link itself. The current state of my code is below, along with short summary of each part. I am not asking you to answer everything at once, just give me some directions and I will slowly implement them and hopefully at the end of the week we will get it to work.
HTML Template *mamp/htdocs/assets/templates/signup_template.html*
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body style="margin: 0; padding: 0;">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td>
Hello {USERNAME}, please click on the link below to finish the registration process
</td>
</tr>
<td>
Link: {SITEPATH}{KEY}
</td>
<tr>
<td>
</td>
</tr>
</table>
</body>
</html>
I assume this is how the HTML template should look like? I take it the site path should be something like this base_url('controller/method/') so that the key is sent as an argument to my function that finishes up the registration.
TXT Template *mamp/htdocs/assets/templates/signup_template.txt*
//I have 0 clue how this file should look like
But ts is to be used when the receiver's email client doesn't support HTML emails.
Controller
$info = array(
'name' => $name,
'email' => $email,
'confkey' => $confkey
);
if ($this->User_m->sendMail($info) == 'error') {
//error reporting
$data['msg'] = "Something wen't wrong";
$this->load->view('registration_v',$data);
} else {
$this->load->view('success_v');
}
This I understand. :D The $info contains the name & email input from my form and the $confkey holds my unique key used for validation. Then the email is sent and if there is an error it gets reported or I load the success_v view.
Model
private function format_email($info, $format){
//set the root
$root = base_url() . 'assets/templates/; //Modified for CI
//I am not sure if I can just keep this code or should also modify it
//grab the template content
$template = file_get_contents($root.'/signup_template.'.$format);
//replace all the tags
$template = ereg_replace('{USERNAME}', $info['name'], $template);
$template = ereg_replace('{KEY}', $info['confkey'], $template);
$template = ereg_replace('{SITEPATH}','http://site-path.com', $template);
//return the html of the template
return $template;
}
This function is called from within the sendMail function to generate the two bodies for the actual email. The function finds the template and replaces the values inside {} with the values provided in arguments. The problem is I don't know if the replacing part will work in CI.
public function sendMail($info){
//loading the CI's mail helper
$ci = get_instance();
$ci->load->helper('mail');
//format each email
$body = this->format_email($info,'html'); //Modified for CI
$body_plain_txt = this->format_email($info,'txt'); //Modified for CI
//setup the mailer
//This is what I need to rewrite for CI's mail helper
$transport = Swift_MailTransport::newInstance();
$mailer = Swift_Mailer::newInstance($transport);
$message = Swift_Message::newInstance();
$message ->setSubject('Welcome to Site Name');
$message ->setFrom(array('noreply#sitename.com<script type="text/javascript">
/* <![CDATA[ */
(function(){try{var s,a,i,j,r,c,l,b=document.getElementsByTagName("script");l=b[b.length-1].previousSibling;a=l.getAttribute('data-cfemail');if(a){s='';r=parseInt(a.substr(0,2),16);for(j=2;a.length-j;j+=2){c=parseInt(a.substr(j,2),16)^r;s+=String.fromCharCode(c);}s=document.createTextNode(s);l.parentNode.replaceChild(s,l);}}catch(e){}})();
/* ]]> */
</script>' => 'Site Name'));
$message ->setTo(array($info['email'] => $info['username']));
$message ->setBody($body_plain_txt);
$message ->addPart($body, 'text/html');
$result = $mailer->send($message);
return $result;
}
And finally the function that actually sends the email. I tried to rewrite what I thought I understand into CI's syntax, but the actual email creation is untouched. If you could tell me how to rewrite this function into CI's mail helper syntax, that would be just awesome.
Thank you all for reading and for your replies. If you would need any additional information ask straight ahead. Also, MERRY CHRISTMAS!
Take a break and start reading this page: http://ellislab.com/codeigniter/user-guide/libraries/email.html
Sending an email with codeigniter is quite simple, your controller can look like this
public function sendEmail() {
$this->load->library('email');
$this->email->from('your#example.com', 'Your Name');
$this->email->to('someone#example.com');
$this->email->cc('another#another-example.com');
$this->email->bcc('them#their-example.com');
$this->email->subject('Email Test');
$this->email->message('Testing the email class.');
$this->email->send();
}
Then if you want to replace values on a view parse the data to the view with $data as you already did here:
$data['msg'] = "Something wen't wrong";
$this->load->view('registration_v',$data);
In this example just echo $msg on registration_v - you could make it the emailaddress entered or another value.
Hopefully this will help you.
So I have my views split up basically between three (3) files:
-- Header file
$this->load->view('templates/header', $data);
-- Main Body file
$this->load->view('login_view', $data);
-- Footer file
$this->load->view('templates/footer', $data);
Now I just recently started building, but I've noticed it's really annoying to retype the header and footer on every controller to tell it to load. Is there a way to automatically load the header and footer view on every request?
I found an article long time ago, but i can't seem to find it now, basically the author, (which i forgot) override the showing of output. this method of output will access your views regarding the given controller/method and will try to search in your views directory automatically.
Use at your own risk
Application/core/MY_COntroller.php
----------------------------------------------------------------------------
class MY_Controller Extends CI_Controller
{
protected $layout_view = 'layouts/application'; // default
protected $content_view =''; //data
protected $view_data = array(); //data to be passed
public function __construct()
{
parent::__construct();
}
public function _output($output)
{
if($this->content_view !== FALSE && empty($this->content_view)) $this->content_view = $this->router->class . '/' . $this->router->method;
$yield = file_exists(APPPATH . 'views/' . $this->content_view . EXT) ? $this->load->view($this->content_view, $this->view_data, TRUE) : FALSE ;
if($this->layout_view)
{
$html = $this->load->view($this->layout_view, array('yield' => $yield), TRUE);
echo $html;
}
}
}
Application/views/layouts/layout.php
----------------------------------------------------------------------------
<html>
<head>
<title>master layout</title>
</head>
<body>
<!-- this variable yeild is important-->
<div><?=$yield;?></div>
</body>
</html>
This is what i use to create my template. Basically you need a directory structure as follows.
+Views
|+layouts
||-layout.php
the layout.php will serve as your master template
How to use?
extend the controller
class User Extends MY_Controller
{
public function create_user()
{
//code here
}
public function delete_user()
{
//use a different master template
$this->layout_view = 'second_master_layout';
}
public function show_user()
{
//pass the data to the view page
$this->view_data['users'] = $users_from_db;
}
}
Just create directory in your views and name it with the controller name i.e user then inside it add a file you named your method i.e create_user
So now your Directory structure would be
+Views
| +layouts
| |-layout.php
| |-second_master_layout.php
| +user
| |-create_user.php
Just Edit the code to give you a dynamic header or footer
Here is the simple example which i always do with my CI project.
Pass the body part as a $main variable on controller's function
function test(){
$data['main']='pages/about_us'; // this is the view file which you want to load
$data['something']='some data';// your other data which you may need on view
$this->load->view('index',$data);
}
now on the view load the $main variable
<html lang="en">
<head>
</head>
<body>
<div id="container">
<?php $this->load->view('includes/header');?>
<div id="body">
<?$this->load->view($main);?>
</div>
<?php $this->load->view('includes/footer');?>
</div>
</body>
</html>
In this way you can always use index.php for your all the functions just value of $main will be different.
Happy codeing
Using MY_Controller:
class MY_Controller extends CI_Controller {
public $template_dir;
public $header;
public $footer;
public function __construct() {
parent::__construct();
$template_dir = 'templates'; // your template directory
$header = 'header';
$footer = 'footer';
$this->template_dir = $template_dir;
$this->header = $header;
$this->footer = $footer;
}
function load_views ($main, $data = [], $include_temps = true) {
if ($include_temps = true) {
$this->load->view('$this->template_dir.'/'.$this->header);
$this->load->view($main);
$this->load->view('$this->template_dir.'/'.$this->footer);
} else {
$this->load->view($main);
}
}
}
Then load it like: $this->load_views('login_view', $data);
You can do with library.
Create a new library file called template.php and write a function called load_template. In that function, use above code.
public function load_template($view_file_name,$data_array=array()) {
$ci = &get_instatnce();
$ci->load->view("header");
$ci->load->view($view_file_name,$data_array);
$ci->> load->view("footer");
}
You have to load this library in autoload file in config folder. so you don't want to load in all controller.
You can call
$this->template->load_template("index",$data_array);
If you want to pass date to view file, then you can send via $data_array
There is an article on this topic on ellislab forums. Please take a look. It may help you.
http://ellislab.com/forums/viewthread/86991/
Alternative way: Load your header and footer views inside the concern body view file. This way you can have batter control over files you want to include in case you have multiple headers and footer files for different purposes. Sample code shown below.
<html lang="en">
<head>
</head>
<body>
<div id="container">
<?php $this->load->view('header');?>
<div id="body">
body
</div>
<?php $this->load->view('footer');?>
</div>
</body>
</html>
I am trying to create a wrapper for the $this->content of a specific Module.
What I have is a main layout (All the modules will follow this layout) which builds the base layout with headers and footers, etc. These will stay the same across all the Modules.
However I want to have modules with a custom layout for their body content. I.e. for custom nav bars, etc.
So with the following structure:
module
/ ModuleName
/ view
/ layout
/ modulelayout.phtml
/ modulename
/ index
/ index.phtml
view
/ layout
/ layout.phtml
The /view/layout/layout.phtml:
<?= $this->doctype(); ?>
<html lang="en">
<head>
...
</head>
<body>
<header>...</header>
<div id='body'>
<?= $this->content; ?>
</div>
<footer>...</footer>
</body>
</html>
The /module/ModuleName/view/layout/modulelayout.phtml:
<div>...</div>
<div>
<?= $this->content; ?>
</div>
The /module/ModuleName/view/modulename/index/index.phtml:
Hello World
...
So I want all of the actions inside of ModuleName (their $this->content that is displayed), to be wrapped with the layout from modulelayout.phtml.
I created a listener on the dispatch event of a controller to capture it for all controller actions:
public function onBootstrap($e) {
$app = $e->getApplication();
$app->getEventManager()
->getSharedManager()
->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', array($this, 'dispatchControllerStrategy'));
}
Now I need to know how to I retain the base layout, and add my module layout as a wrapper?
public function dispatchControllerStrategy($e) {
$controller = $e->getTarget();
$layout = $controller->layout();
$wrapper = new ViewModel();
$wrapper->setTemplate('layout/modulelayout');
$layout->addChild($wrapper, 'content');
}
^^^ The adding of the child layout does not seem to wrap $this->content in any way, and the child layout does not render. To bring it all together, this is what I expect the final source to look like:
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<header>...</header>
<div id='body'>
<div>...</div>
<div>
Hello World
...
</div>
</div>
<footer>...</footer>
</body>
</html>
Thanks!
Well after a lot of messing around I finally found my solution. And it appears that comment of SlmThreeStepView user2257808 was what I wanted to begin with, but my solution fit my need exactly so I am just going to share it here:
I stopped worrying about trying to modify the layout on controller dispatch and focused on the View with the EVENT_RENDERER_POST event:
->attach('Zend\View\View', \Zend\View\ViewEvent::EVENT_RENDERER_POST, array($this, 'renderViewStrategy'));
I then modify my model on render:
private $renderdedOuterView = false;
public function renderViewStrategy($e) {
if ($this->renderdedOuterView) {
return;
} else {
$this->renderdedOuterView = true;
}
$layout = $e->getModel();
$children = $layout->getChildren();
$layout->clearChildren();
$wrapper = new ViewModel();
$wrapper->setTemplate('layout/modulelayout');
$wrapper->addChild($children[0], 'content');
$layout->addChild($wrapper, 'content');
}
I used $renderedOuterView to only do any render modification for the main layout.
Then for the main layout, I get the Model, and grab the attached child which would be the layout for the current action.
I then clear the main template of that child and add my wrapper in its place. Then as a child of my wrapper, I add the layout for the current action I had just removed from the main template.
My not be the best option, but this solution fits exactly what I was trying to accomplish with my question.
UPDATE
I wanted to add that I found out another issue.
All the onBootstraps for each Module get called, regardless if they are the active module.
This was causing a different module to get this layout. The change was adding an EVENT_DISPATCH first:
$evtMgr = $e->getApplication()->getEventManager();
$evtMgr->attach(MvcEvent::EVENT_DISPATCH, array($this, 'handleEventStrategy'));
Then inside of the handleEventStrategy, I check to make sure that the active Module is the module's bootstrap that is being called. If it is, then I attach the EVENT_RENDER_POST and use the renderViewStrategy that I had defined.
If the active module is not the module with the render view strategy, the if condition will fail and no other module will get that modified layout.
Did you try this module: https://github.com/EvanDotPro/EdpModuleLayouts
And then add the config in your module config file
array(
'module_layouts' => array(
'ModuleName' => 'layout/some-layout',
),
);
This is more of a style question. I have a template file header.php in which I define a PrintHeader() function.
Callers of this function can specify, via global variables, the title of the page and any Javascript scripts to include when printing the header (because surely not every page will have the same title or want to include the same scripts). I chose to use global variables rather than function arguments because the latter would require the interface to change when adding new arguments.
Is this considered "good" style, and is there a "better" way to do what I'm trying to do?
header.php (simplified)
<?php
function PrintHeader()
{
global $pageTitle, $scripts; // Set by the caller of this function
echo <<<HEADER
<html>
<head>
<title>$pageTitle</title>
HEADER;
if( !empty($scripts) )
{
foreach($scripts as $script)
{
echo " <script type=\"text/javascript\" src=\"$script.js\"></script>\n";
}
}
echo " </head>\n";
}
?>
index.php (simplified)
<?php
$pageTitle = 'Welcome';
$scripts = array('script1', 'script2');
require('header.php');
PrintHeader();
// Print the rest of the page
?>
is there a "better" way to do what I'm trying to do?
sure.
I see no point in defining and calling a function at all. as well as in using heredoc.
header.php (dramatically simplified):
<html>
<head>
<title><?=$pageTitle?></title>
<? if( !empty($scripts) ): ?>
<? foreach($scripts as $script): ?>
<script type="text/javascript" src="<?=$script?>.js"></script>
<? endforeach ?>
<? endif ?>
</head>
index.php:
<?php
$pageTitle = 'Welcome';
$scripts = array('script1', 'script2');
require('header.php');
?>
but still it's not the best way, as it seems you're not using a template where it most valuable - to output page contents itself.
So, I'd make it in three parts:
links.php (simplified):
<?
//include our settings, connect to database etc.
include dirname($_SERVER['DOCUMENT_ROOT']).'/cfg/settings.php';
//getting required data
$DATA = getdata("SELECT * FROM links");
$pagetitle = "Links to friend sites";
//etc
//and then call a template:
$tpl = "links.tpl.php";
include "main.tpl.php";
?>
where main.tpl.php is your main site template, including common parts, like header, footer, menu etc:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My site. <?=$pagetitle?></title>
</head>
<body>
<div id="page">
<? include $tpl ?>
</div>
</body>
</html>
and finally links.tpl.php is the actual page template:
<h2><?=$pagetitle?></h2>
<ul>
<? foreach($DATA as $row): ?>
<li><?=$row['name']?></li>
<? endforeach ?>
<ul>
notice native HTML syntax, which is highlighted, readable and centralized in one place instead of being split between numerous functions and files
The point is in having separate template for the every PHP page as well as main site template for them all. With such setup you'll get a lot of advantages such as custom error pages, multiple representations of the same data (say, HTML, JSON or XML) by switching only templates without changing the code and many more
The use of global variables is certainly not advisable, and I question the necessity of using heredoc as you have - not that there is anything inherently wrong with heredoc, just that you seem to have rather arbitrarily utilized it in this sample template.
It is not elegant to use a return-value of a function as the output of each template - this defeats one of the purposes of templates which is re-usability.
Take a look at smarty, if not to directly use it (after all, why re-invent the wheel), at least to get an idea of how a rendering class is used to shuttle in the variables that a template needs without resorting to messy globals.
Here's a very quick overview of a way to do templating:
You have a template class that you can assign data to and then render a template.
Template.php:
class Template
{
protected $data = array();
public function assign($key, $value)
{
$this->data[$key] = $value;
}
public function render($file)
{
extract($this->data);
require $file;
}
}
You then have your template, header.php:
<html>
<head>
<title><?php echo $pageTitle; ?></title>
....
In index.php, you then use the template class to assign data and render your template.
$tpl = new Template;
$tpl->assign('pageTitle', 'My page title!');
$tpl->render('header.php');
This is just a simple example to demonstrate the idea, and could give you a good starting point.
While "better" may be in the eye of the beholder, I would suggest having some sort of functions that set the page bits rather than exposing raw variables. For instance, instead of doing $pageTitle = 'Welcome'; you could have set_page_title('Welcome');.
For JavaScript you could have a function that adds to the current script set -- rather than possibly replacing it all -- such as add_javascript($code);. This will allow a developer to set all of these without having to keep track of what the variable name was, and also without needing to global it as well if they want to set it from within a function.
This is an alternative using output buffering.
p/example_page/index.php is one of your pages:
<?php
ob_start() ?>
<h1>Example</h1>
<p>This is the page content</p>
<?php $main = ob_get_clean();
ob_start() ?>
<script defer src="js/example_page/example.js"></script>
<?php $script = ob_get_clean();
$title = 'Example page';
include 'templates/base.php';
templates/base.php is your reusable layout:
<!doctype html>
<html>
<head>
<script defer src="js/main.js"></script>
<?php echo $script ?>
<title><?php echo $title ?> - Example website</title>
</head>
<body>
<header>
<nav aria-label="Main menu"></nav>
</header>
<main><?php
echo $main;
?></main>
<footer>Example footer</footer>
</body>
</html>
Global variables are generally considered bad, and should be avoided if possible.
Rather than listing every variable in the interface, as you said things could change, pass a single array to the PrintHeader() functions:
<?php
function PrintHeader($opts=array()) {
if(!isset($opts['title'])) $opts['title'] = 'Default Title';
echo <<<HEADER
<html>
<head>
<title>$opts['title']</title>
HEADER;
if(!empty($opts['scripts'])) {
foreach($opts['scripts'] as $script) {
echo " <script type=\"text/javascript\" src=\"$script.js\"></script>\n";
}
}
echo " </head>\n";
}
$opts = array('title'=>'Welcome',
'scripts'=>array('script1', 'script2'));
require('header.php');
PrintHeader($opts);
?>
This way, you can add new capabilities in the function without breaking old code.