This is another of those utilities where every blogger\tutor assumes we all know what it is, therefore they never bother to explain what it actually does. They just go on to use it in their examples, believing we all know what's going on.
From how people use or refer to render(), it would suggest that it displays content(eg: view content) but if this is so, then why do we use echo to actually display it's content?
Other uses suggests that it formats content, as used in form-decorators where internally we employ sprintf() to inject variables into strings.
So what does render() do in cases like Zend_View, Zend_Layout, etc? Can someone please explain it's workings on a fundamental level(under the hood). Thanks.
It loads a view script and outputs it as a string.
Simplifying a bit, Zend_View takes a view script file (like index.phtml) and includes it internally to produce the HTML output. By using the render() method, it's possible to take an additional view script (like maybe nav.phtml) and output it inside your parent view script. The idea is to render elements that are repeated on many pages just one single time instead of repeating the same HTML over and over again.
The code for the render method can be found in the Zend_View_Abstract class and is the following:
/**
* Processes a view script and returns the output.
*
* #param string $name The script name to process.
* #return string The script output.
*/
public function render($name)
{
// find the script file name using the parent private method
$this->_file = $this->_script($name);
unset($name); // remove $name from local scope
ob_start();
$this->_run($this->_file);
return $this->_filter(ob_get_clean()); // filter output
}
The implementation of the _run() method can be found in the class Zend_View and is the following:
/**
* Includes the view script in a scope with only public $this variables.
*
* #param string The view script to execute.
*/
protected function _run()
{
if ($this->_useViewStream && $this->useStreamWrapper()) {
include 'zend.view://' . func_get_arg(0);
} else {
include func_get_arg(0);
}
}
As you can see, render() takes a view script name, resolves its file name, initiates output buffering, includes the view script file (this is what the _run() method does internally), then passes the output through optional filters and finally returns the generated string.
The neat thing about it is that it retains the properties (variables) of the view object it is called from (because it's the same Zend_View object, just with a different view script loaded). In this respect, it differs from the partial() method, which has its own variable scope and you can pass variables into it (making it useful for rendering smaller elements, such as single rows of data when you foreach over a dataset).
Related
I want to move a column from one tab to another. Is there a better way than to rewrite the types definition manually which is not very reliant in regard to extension updates changing the TCA?
It's not 100% fail safe, since there still might be extensions messing things up, but the closest you can get will be to make use of the ExtTablesInclusionPostProcessing event and put addToAllTCAtypes into the invoke method, since this will not just add new fields but even put existing fields to another tab:
<?php
declare(strict_types=1);
namespace Vendor\Extension\EventListener;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* Class ExtTablesInclusionPostProcessing
*/
class ExtTablesInclusionPostProcessing
{
/**
* Function which may process data created / registered by extTables
* scripts (f.e. modifying TCA data of all extensions)
*
* #param AfterTcaCompilationEvent $event
*/
public function __invoke(AfterTcaCompilationEvent $event): void
{
ExtensionManagementUtility::addToAllTCAtypes('tt_content', 'fieldname', '', 'after:fieldname_in_the_other_tab');
}
}
To remove the existing field before, you could make use of this trick:
https://stackoverflow.com/a/59179916/2333754
And finally to be on the safer side you could make sure that other extensions are loaded before your own extension by putting them into the suggest section of your composer.json.
With installation of extension tcabuilder it is possible to do simple changes in the TCA.
What I would like is to write something like this:
/**
* Takes a foo and {#link grokelates} it.
*/
function doSomething(foo) {
}
And have "grokelates" be a link to more detail on what "grokelate" means, but because I'm going to have functions dealing with grokelation all over my code base, I'd like to write that definition once and link to it in multiple places.
Is this possible?
To be clear, grokelates is not a function. It's just a word I want to define, but not have to define in-line everywhere I use it. I basically want to write a glossary file and be able to link to definitions from that glossary in my JSDoc.
Ideally this would also be in a way the VS Code picks it up and lets someone navigate to that definition on hover.
Yes there is. When you run jsdoc to generate your documentation, you can pass it any filetype you wish. A standard practice is to create one or more *.jsdoc files which contain doclet comments (those that begin with /**) to describe features you expect to use elsewhere in your code. For instance:
// filename: grokelation.jsdoc
/**
* #module grokelates
*/
/**
* #name Grokelate
* #memberof module:grokelates
* #description
* Here is the description of the grokelation process.
*
* #example
* var g = new Grokelate(opts);
*/
Then, when you wish to reference this new object elsewhere in your documentation, simply use its long name module:grokelates~Grokelate where you can consider the ~ glyph to mean "member of".
In your example above, you'd say {#link module:grokelates~Grokelate}.
I want to create an "extension" for a Jacada Interaction (to extend functionality), in my case to parse and assign the numerical part of serialNumber (a letter, followed by digits) to a numeric global ("system") variable, say serialNumeric. What I am lacking is the structure and syntax to make this work, including the way to reference interaction variables from within the extension.
Here is my failed attempt, with lines commented out to make it innocuous after failing; I think I removed "return page;" after crashing, whereupon it still crashed:
initExtensions("serialNumeric", function(app){
app.registerExtension("loaded", function(ctx, page) {
// Place your extension code here
//$('[data-refname="snum"]').val('serialNumber');
// snum = Number(substring(serialNumber,1))
});
});
Here is an example of one that works:
/**
* Description: Add swiping gestures to navigate the next/previous pages
*/
initExtensions("swipe", function(app) {
// Swipe gestures (mobile only)
app.registerExtension('pageRenderer', function(ctx, page) {
page.swipe(function(evt) {
(evt.swipestart.coords[0] - evt.swipestop.coords[0] > 0)
? app.nextButton.trigger('click')
: app.backButton.trigger('click')
});
return page;
});
});
After reading the comment below, I tried the following, unsuccessfully (the modified question variable is not written back to that variable). It rendered poorly in the comment section, so I am putting it here:
initExtensions("serialNumeric", function(app){
app.registerExtension("loaded", function(ctx, page) {
var sernum = new String($('[data-refname="enter"] input'));
var snumeric = new String(sernum.substr(1));
$('[data-refname="enter"] input').val(snumeric);
});
});
I would like to understand when this code will run: it seems logical that it would run when the variable is assigned. Thanks for any insight ~
In your case, you extend loaded event. You don't have to return the page from the extension like in your working example below.
The page argument contains the DOM of the page you have just loaded, the ctx argument contains the data of the page in JSON form. You can inspect the content of both arguments in the browser's inspection tools. I like Chrome. Press F12 on Windows or Shift+Ctrl+I on Mac.
The selector $('[data-refname="snum"] input') will get you the input field from the question with the name snum that you defined in the designer. You can then place the value in the input field with the value from the serialNumber variable.
$('[data-refname="snum"] input').val(serialNumber);
You can also read values in the same way.
You can't (at this point) access interaction variables in the extension, unless you place theses variables inside question fields.
Here is a simple example how to put your own value programmatically into a input field and cause it to read it into the model, so upon next it will be sent to the server. You are welcome to try more sophisticated selectors to accommodate for your own form.
initExtensions("sample", function(app){
app.registerExtension("loaded", function(ctx, page) {
// simple selector
var i = $('input');
// set new value
i.val('some new value');
// cause trigger so we can read into our model
i.trigger('change');
});
});
I know there´s a similar situation in stack, and I´ve already checked it out and the answers have not guided me to mine.
Here´s the deal:
I need to execute raw SQL, an INSERT to be precise. I have multiple values to insert as well. No problem since Zend let´s you use "query" from Zend_Db_Table (I use my default adapter, initialized in my application.ini file).
$db_adapter = Zend_Db_Table::getDefaultAdapter();
....query gets built...
$stmt = $db_adapter->query($query);
$stmt->execute();
When doing var_dump($query); this is what I see:
INSERT INTO tableName (col1,col2,col3) VALUES ('value1', 'value2', 'value3')
I have already tried the following:
I have no "manifesto" attribute on the html page rendered
I have looked at the network both with Chrome and Firefox to see the POSTS/GETS getting sent, and the controller whose actions does
this INSERT gets called once with a POST.
It is true that I call a viewscript from another action to be rendered, like so:
$this->renderScript('rough-draft/edit.phtml');
I don´t see how that could affect the statement getting executed twice.
I know it gets executed twice:
Before the POST is sent, the data base has nothing, clean as a whistle
POST gets sent, logic happens, from is rendered again
Data base now has the same record twice (with the name that has been inserted from the POST), so it´s not a rendering problem
It has to do with the way I am executing raw SQL, because if I use ->insert($data) method that comes with Zend_Db_Table_Abstract (obviously I declare a class that extends from this abstract one and bind it to the appropriate table) it does not insert the record twice. Thing is, I want to use raw SQL.
I really appreciate your help!
Expanded as requested:
Controller code:
public function saveAction()
{
$request = $this->getRequest();
if (!$request->isPost())
$this->_sendToErrorPage();
$this->_edit_roughdraft->saveForm($request->getPost());
$this->view->form = $this->_edit_roughdraft->getForm();
$this->renderScript('rough-draft/edit.phtml');
}
Code from model class (in charge of saving the form from the data in POST):
public function saveForm($form_data) {
$db = new Application_Model_DbTable_RoughDrafts();
$id = $form_data['id'];
$db->update($this->_get_main_data($form_data), array('id=?' => $id));
/* Main data pertains to getting an array with the data that belongs to yes/no buttons
* and text input. I need to do it as so because the other data belongs to a dynamically
* built (by the user) multi_select where the options need to be deleted or inserted, that
* happens below
*/
$multi_selects = array('responsables', 'tareas', 'idiomas', 'habilidades', 'perfiles');
foreach($multi_selects as $multi_select){
if(isset($form_data[$multi_select]) && !empty($form_data[$multi_select])){
$function_name = '_save_'.$multi_select;
$this->$function_name($form_data[$multi_select], $id);
}
}
$this->_form = $this->createNewForm($id, true);
//The createNewForm(..) simply loads data from data_base and populates form
}
I do want to make clear though that I have also used $db->insert($data), where $db is a class that extends from Zend_Db_Table_Abstract (associated to a table). In this case, the record is inserted once and only once. That´s what leads me to believe that the problem lies within the execution of my raw sql, I just don´t know what is wrong with it.
I should have done this from the beginning, thanks for the help everyone, I appreciate your time.
I knew the problem was with my syntax, but I didn´t know why. After looking at different examples for how people did the raw sql execution, I still didn´t understand why. So I went ahead and opened the class Zend_Db_Adapter_Abstract in Zend library and looked for the 'query()' method I was using to get the statement to execute.
public function query($sql, $bind = array())
{
...some logic...
// prepare and execute the statement with profiling
$stmt = $this->prepare($sql);
**$stmt->execute($bind);**
// return the results embedded in the prepared statement object
$stmt->setFetchMode($this->_fetchMode);
return $stmt;
}
So the ->query(..) method from the Zend_Db_Adapter already executes said query. So if I call
->execute() on the statement that it returns, I'll be the one causing the second insert.
Working code:
$db_adapter = Zend_Db_Table::getDefaultAdapter();
....query gets built...
$db_adapter->query($query);
I need to know how to customize my contact and register forms. How to add new fileds ( and ) and make the information from these fields required or not required.
I need to know which files I must edit for these forms...
I use prestashop 1.4.7.0
This is really two separate questions as there are major differences in how you would handle each case.
Answer 1
For the registration form you can write a module which contains two hook handler functions. These will be:
public function hookCreateAccountForm() {}
public function hookCreateAccount($params) {}
The first function allows you to add additional fields to the registration form (by default these are inserted at the end of the form authentication.tpl, although you could move them all as a single group elsewhere). It should simply return the additional form html you require.
The second function provides you with two parameters to handle the account creation process. This is executed after the standard fields have been validated and the new customer has been created. Unfortunately you cannot do validation on your additional fields using this (you would need to either use javascript or override AuthController to perform your own authentication in the preProcess() member function). In one of my own custom modules for a site I have the following, for example:
public function hookCreateAccount($params)
{
$id_lang = (int)Configuration::get('PS_LANG_DEFAULT');
$customer = $params['newCustomer'];
$address = new Address(Address::getFirstCustomerAddressId((int)$customer->id));
$membership_number = $params['_POST']['membership_number'];
....
....
}
$params['newCustomer'] is a standard Prestashop element in the array and contains the newly created customer object. Your fields will be in the $params['_POST'] array - in my case it was an input field called membership_number.
Answer 2
For the contact form it's a whole lot more complicated I'm afraid. The simplest method for the html is to just hard-code your additional fields in the template file contact-form.tpl.
To actually process the form you will need to create an override for the controller by ceating a file called ContactController.php in /<web-root>/<your-optional-ps-folder>/override/controller containing something like:
<?php
class ContactController extends ContactControllerCore {
function preProcess()
{
if (Tools::isSubmit('submitMessage'))
{
// The form has been submitted so your field validation code goes in here.
// Get the entered values for your fields using Tools::getValue('<field-name>')
// Flag errors by adding a message to $this->errors e.g.
$this->errors[] = Tools::displayError('I haven't even bothered to check!');
}
parent::preProcess();
if (Tools::isSubmit('submitMessage') && is_empty($this->errors))
{
// Success so now perform any addition required actions
// Note that the only indication of success is that $this->errors is empty
}
}
}
Another method would be to just copy the entire preProcess function from controllers\ContactController and just hack away at it until it does what you want....