SCENARIO
There are times when a developer comes across a scenario / situation where he have a need to copy the existing content of a row in table to a new form. There might be just minor changes in the same. Rest all would be perfectly the same as it is for the previous row.
SOLUTION
To achieve the same, here is a set solution.
Find the following
class grocery_CRUD_States extends grocery_CRUD_Layout
……Then add the following row
const COPY_ROW = '20'; //This represent a new state that we need to handle in grocery crud
Find the following (just in that same class)
protected $states = array()
….Add the following
20 => 'copy'
Find the following function
public function getStateInfo() { ... }
…..Add the following case ..
case self::COPY_ROW: // copy
if($first_parameter !== null)
{
$state_info = (object)array('primary_key' => $first_parameter);
} else {
throw new Exception('On the state "copy" the Primary key cannot be null', 6);
die();
}
break;
///Here we need to have the primary key for us in order to copy the record
Now find the following
public function render() { .. }
…. Add the following piece of code in the same
case grocery_CRUD_States::COPY_ROW://copy
if($this->unset_add)
{
throw new Exception('You don't have permissions for this operation', 14);
die();
}
if($this->theme === null)
$this->set_theme($this->default_theme);
$this->setThemeBasics();
$this->set_basic_Layout();
//Set the default values
$state_info = $this->getStateInfo();
$this->copy_row($state_info);
$this->showAddForm();
break;
………..Just below the same function add the following
protected function copy_row($state_info) {
$fields = $this->get_add_fields();
$types = $this->get_field_types();
$field_values = $this->get_edit_values($state_info->primary_key);
foreach($fields as $field)
{
$fieldName = $field->field_name;
if(property_exists($field_values,$fieldName)) {
if($types[$fieldName]->crud_type == 'upload_file') {
//Make a copy of the same and then assign the new value to the same
$oldFileName = $field_values->{$fieldName};
$oldFileName = substr($oldFileName, 6);
$newFileName = substr(uniqid(),-5).'-'. $oldFileName;
$sourcePath = $types[$fieldName]->extras->upload_path . DIRECTORY_SEPARATOR . $field_values->{$fieldName};
$newPath = $types[$fieldName]->extras->upload_path . DIRECTORY_SEPARATOR . $newFileName;
if(is_file($sourcePath)) {
copy($sourcePath, $newPath);
$this->getModel()->set_add_value($field->field_name, $newFileName);
}
} else {
$this->getModel()->set_add_value($field->field_name, $field_values->{$fieldName});
}
}
}
}
Now find the following
protected function showAddForm() {..}
…. in here add the following
$data->field_values = $this->get_add_values(null);
…….Now find the following and comment the line
$data->input_fields = $this->get_add_input_fields();
….instead — add the following
$data->input_fields = $this->get_add_input_fields($data->field_values);
…..Now alter the following function .. just find and replace it with the one below
protected function get_add_input_fields($field_values = null)
{
$fields = $this->get_add_fields();
$types = $this->get_field_types();
$input_fields = array();
foreach($fields as $field_num => $field)
{
$field_info = $types[$field->field_name];
$field_value = !empty($field_values) && isset($field_values->{$field->field_name}) ? $field_values->{$field->field_name} : null;
if(!isset($this->callback_add_field[$field->field_name]))
{
$field_input = $this->get_field_input($field_info, $field_value);
} else {
$field_input = $field_info;
$field_input->input = call_user_func($this->callback_add_field[$field->field_name], $field_value, null, $field_info);
}
switch ($field_info->crud_type) {
case 'invisible':
unset($this->add_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
case 'hidden':
$this->add_hidden_fields[] = $field_input;
unset($this->add_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
}
$input_fields[$field->field_name] = $field_input;
}
return $input_fields;
}
/*********************Noote 1 thing – u need to alter the grocery_crud_model and add following function there *******/
function set_add_value($field_name, $value) {
$this->add_values[$field_name] = $value;
}
function get_add_values() {
return (object) $this->add_values;
}
Please note: This piece of code is written a long time ago. In case there are any issues with the same, do let me know, will surely patch it with a fix.
Usually i would have had left solution without any explanations.. But i intend to go ahead and share my workaround on this scenario. GroceryCrud never was built up with a situation for copying a row. Sorry, don’t remember the name, but there was one contributor who came up with brilliant solution for setting default value for the add option. That is what clicked me to arrive to this solution.
Now What you mean by Copying the row – when needed – what we do is.. read the values from the previous row (the one to be copied) and set the values as default values to the new add form. That’s how this hack was built around.
GroceryCrud is designed to work around multiple functionalities from single controller’s method call (One may refer to the same as actions). This one is a well designed mini router defined around a controllers method. So what we do .. we add up our own route definition – const COPY …
Well, the actions (mentioned above) in GC are referred to as States. So we add our own state to the same.
Also add our piece of code where it retrieves the stateInfo.
Post setting up the StateInfo & State, we need to write our own handler for the state. The same is handled in the render method of the library. So we go and add our handler for the case (Copy) in the render method.
One of the functionality at the core what we have is – copy_row. There what we do is pull the value from the old record (the one to be copied from), and for each fields, set the relevant values to the new fields. Also takes care of the upload field. It don’t map the same value, but it rather copies file to new path and set the new file value to the same.
We additionally need to alter the GCModel file to set / get add field values.
One more thing before we show the add form..
We need to alter the input fields being generated to set the default values. That is where we alter it with get_add_input_fields.
Rest is all the game of updating the ShowAddForm where it makes the relevant calls to the required methods defined up earlier in the process.
And there you have your Copy Functionality baked and served.
Happy GCing 🙂