Overwriting JControllerForm save() Method to Trim POST Data Has No Effect - joomla

I have a component that has a controller called MyproductControllerGeneralsetting which extends JControllerForm. Inside MyproductControllerGeneralsetting I am overwriting the save method from the parent class in order to modify $_POST data and then the overwriting method calls the parent class' save method to do the actual saving.
Here's the overwritten method in MyproductControllerGeneralsetting:
/**
* We overwrite the saved form data and trim them to avoid spaces
*/
public function save($key = null, $urlVar = null){
if($_POST['jform']){
foreach($_POST['jform'] as $key=>&$value){
$value = trim($value);
}
}
// Finally, save the processed form data (calls JControllerForm-save())
parent::save('id', $urlVar);
}
The thing is that even though I've trimmed each POST data field in this overwriting method, if I have some values submitted such as 'value ' (note the space in the end), they are not trimmed.
I've checked the save method of the JControllerForm class and it seems to be getting the data from POST here:
$data = $this->input->post->get('jform', array(), 'array');
Maybe that's the reason? Is this getting cached data or something?

Instead of trying to get the values from $_POST directly, try getting and setting the data in the same way the parent class does - using an internal pointer to a (shared) instance of the JInput class.
Here's a modified, working, overwritten save method:
/**
* We overwrite the saved form data and trim them to avoid spaces
*/
public function save($key = null, $urlVar = null){
if($_POST['jform']){
// Get the original POST data
$original = JRequest::getVar('jform', array(), 'post', 'array');
// Trim each of the fields
foreach($original as $key=>$value){
$original[$key] = trim($value);
}
// Save it back to the $_POST global variable
JRequest::setVar('jform', $postData, 'post');
}
// Finally, save the processed form data
return parent::save('id', $urlVar);
}

The controller is the wrong place for such things anyway, or is there a specific reason you want to do it in the controller?
Better look at the prepareTable function in the model. There you already have the table object with the properties to save and can sanitise them prior to saving.
Additional Info:
If you extend JControllerForm, you can specify
/**
* #since 1.6
*/
protected $view_item = 'item';
/**
* #since 1.6
*/
protected $view_list = 'items';
By default, the $view_item will equal to the context. The $view_list tries to guess a pluralized version of the $view_item. Usually by adding an s to the end.

Related

How to overwrite bulk clone function for laravel backpack

I am trying to overwrite a bulk clone function.
I just want to clone some values, and the rest assign a static value. For example, I just want to clone the name and description values, and the date I want to assign it the current date.
And well I don't know how to do it.
use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation { bulkClone as traitBulkClone; }
public function bulkClone($id) {
// your custom code here
$this->traitBulkClone($id);
}
TLDR: The most efficient way would probably be to overwrite the replicate() method on your model. Note that it is not a Backpack method, but an Eloquent method that BulkCloneOperation uses to duplicate a particular entry.
WHY?
Inside the BulkCloneOperation that you're using, you'll notice the route calls the bulkClone() method, that itself is just making some calls to the replicate() method on the model. That means you have two options to override this behaviour:
(Option A). Override the bulkClone() method in your CrudController. This will override the behaviour only on that particular admin operation.
(Option B). Override the replicate() method in you Model. That way, any time replicate() is called (by your admin panel or any other part of your software), the duplication is done in the way you specified.
In most cases, I think Option B is more appropriate, since it would avoid future code duplication. Here's Laravel's replicate() method at this time, just copy-pasting it into your model and modifying it to fit your needs is the best solution, if you ask me:
/**
* Clone the model into a new, non-existing instance.
*
* #param array|null $except
* #return static
*/
public function replicate(array $except = null)
{
$defaults = [
$this->getKeyName(),
$this->getCreatedAtColumn(),
$this->getUpdatedAtColumn(),
];
$attributes = Arr::except(
$this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults
);
return tap(new static, function ($instance) use ($attributes) {
$instance->setRawAttributes($attributes);
$instance->setRelations($this->relations);
$instance->fireModelEvent('replicating', false);
});
}

Unable to replace files in request

I have an odd one.. I add an audio file, which is an existing file in the request
$file = $request->file('file')[0];
$request->files->add(['audio' => $file]);
When I do dd($request->files->all()) I see newly added 'audio' element. However when I do dd($request->allFiles()) I see original array.
I have even added a replace method for files to the Illuminate\Http\Request to replace $files with no luck.
public function replaceFiles($files) {
$this->files = $files;
}
Am I missing something?
It is as if $request->files and $this->files within $request are two different things.
Just noticed that the problem is with $this->convertedFiles within Illuminate\Http\Request which can be resolved by changing allFiles() to something similar to
/**
* Get an array of all of the files on the request.
*
* #return array
*/
public function allFiles()
{
$files = $this->files->all();
return $this->convertedFiles && count($this->convertedFiles) == count($this->files->all())
? $this->convertedFiles
: $this->convertedFiles = $this->convertUploadedFiles($files);
}
I've decided to extend App\Http\Requests\Request with adjusted allFiles() that I have mentioned above
You can use merge function to add new data into the request object.
$request->merge(['index_name' => $variable]);

Property $_elements of Doctrine\ArrayCollection class change, unable to unserialize old object in database

The property
private $_elements
of Doctrine\Common\Collections\ArrayCollection was renamed to
private $elements
But i had serialized object ArrayCollection in my database. So now, my array collection is empty. (When i dump it, i can see $_elements is not empty) I want to know how to make a command to change that.
I have already try:
replace function with mysql but i have an Notice: unserialize() error
with ReflectionObject, but property $_elements is not defined
runkit_method_redefine, I don't have access
If your ArrayCollection uses only scalar elements inside then this works well:
//use following line only if you already have an unserialzed ArrayCollection
$data = serialize($data);
$data = unserialize($data);
$data = var_export($data, true);
$data = preg_replace('#[A-Za-z\\\]*::__set_state#', '', $data);
$data = eval('return '.$data.';');
$data = new ArrayCollection($data['_elements']);
If you had stored some other classes inside your ArrayCollection, they will be converted to normal arrays.
Warning: Please don't use this code in production, only to recover your serialized data, because eval is evil and is a high potentially security issue.

How to override save() method in a component

Where and how I am overriding the save method in Joomla 3.0 custom component ?
Current situation:
Custom administrator component.
I have a list view that displays all people stored in table.
Clicking on one entry I get to the detailed view where a form is loaded and it's fields can be edited.
On save, the values are stored in the database. This all works fine.However, ....
When hitting save I wish to modify a field before storing it into the database. How do I override the save function and where? I have been searching this forum and googled quiet a bit to find ways to implement this. Anyone who give me a simple example or point me into the right direction ?
Thanks.
Just adding this for anyone who wants to know the answer to the question itself - this works if you explicitly wish to override the save function. However, look at the actual solution of how to manipulate values!
You override it in the controller, like this:
/**
* save a record (and redirect to main page)
* #return void
*/
function save()
{
$model = $this->getModel('hello');
if ($model->store()) {
$msg = JText::_( 'Greeting Saved!' );
} else {
$msg = JText::_( 'Error Saving Greeting' );
}
// Check the table in so it can be edited.... we are done with it anyway
$link = 'index.php?option=com_hello';
$this->setRedirect($link, $msg);
}
More details here: Joomla Docs - Adding Backend Actions
The prepareTable in the model (as mentioned above) is intended for that (prepare and sanitise the table prior to saving). In case you want to us the ID, though, you should consider using the postSaveHook in the controller:
protected function postSaveHook($model, $validData) {
$item = $model->getItem();
$itemid = $item->get('id');
}
The postSaveHook is called after save is done, thus allowing for newly inserted ID's to be used.
You can use the prepareTable function in the model file (administrator/components/yourComponent/models/yourComponent.php)
protected function prepareTable($table)
{
$table->fieldname = newvalue;
}

Magento pass current product ID to module

I have a custom module that displays data on my product pages. My module needs to get the current product id. I tried using:
Mage::registry('current_product');
This works on the first load but when I refresh, current_product no longer has data with full page caching on.
Any ideas?
The catalog product action controller isn't dispatched, when the full page cache handles the request (which makes sense to keep things fast).
So, the registry variables are never set.
I assume you are generating the block dynamically on the otherwise fully cached page.
My recommendation is to try to avoid expensive loads, since that would undermine the speed improvements by the full page cache. You really want to cache the block if at all possible, even if it's a separate cache entry for each customer and each product.
That said, here is how to do that:
In the container, implement the _getIdentifier() method:
protected function _getIdentifier()
{
return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
}
Also expand the _getCacheId() method to include the return value of the method _getIdentifier() and a new placeholder attribute: product_id
protected function _getCacheId()
{
return 'HOMEPAGE_PRODUCTS' . md5($this->_placeholder->getAttribute('cache_id') . ',' . $this->_placeholder->getAttribute('product_id')) . '_' . $this->_getIdentifier();
}
Next, in the block class, extend the method getCacheKeyInfo(). All entries in the cache_key_info array with a string index are set on the placeholder as attributes. That is how we can pass the product id to the placeholder.
public function getCacheKeyInfo()
{
$info = parent::getCacheKeyInfo();
if (Mage::registry('current_product'))
{
$info['product_id'] = Mage::registry('current_product')->getId();
}
return $info;
}
Then enable the method _saveCache() by not overriding it in your container class and returning false.
So now, because the container returns a valid id from _getCacheId(), and _saveCache() is inherited from the parent class, the the block can be cached, and applied to the content in an efficient manner in Enterprise_PageCache_Model_Container_Abstract::applyWithoutApp().
You can set the lifetime of the cache entry by having your container extend from Enterprise_PageCache_Model_Container_Customer instead of Enterprise_PageCache_Model_Container_Abstract.
If you still need to pass the product_id to the block (even though it's cached now), you can do so in the _renderBlock() method of your container:
protected function _renderBlock()
{
$blockClass = $this->_placeholder->getAttribute('block');
$template = $this->_placeholder->getAttribute('template');
$block = new $blockClass;
$block->setTemplate($template)
->setProductId($this->_placeholder->getAttribute('product_id'));
return $block->toHtml();
}
you should have in the controller of your module something which init the registration of the current product. You will need to post between the page visited by a user the id of your product (recommended) or save it in a session. Here is an example of what you can do in a controller:
protected function _initAction(){
if(! Mage::registry('current_product')){
Mage::register('current_product', Mage::getModel('catalog/product'));
}
$id = $this->getRequest()->getParam('id');
if (!is_null($id)) {
$model = Mage::registry('current_product')->load($id);
if (! $model->getId()) {
$this->_getSession()->addError(Mage::helper('catalog')->__('This product item no longer exists'));
$this->_redirect('*/*/');
return;
}
}
return $this;
}
public function indexAction()
{
$this->_initAction();
// your code below
}

Resources