Esempio n. 1
0
 /**
  * Edit an objects of a given CtrlClass, if an ID is given
  * Or renders a blank form if not
  * This essentially renders a form for the object
  * @param  integer $ctrl_class_id The ID of the class we're editing
  * @param  integer $object_id The ID of the object we're editing (zero to create a new one)
  * @param  string $filter_string Optional list filter; such as 43,1, which will set the value of the ctrl_property 43 to 1 when we save the form
  *
  * @return Response
  */
 public function edit_object($ctrl_class_id, $object_id = NULL, $filter_string = NULL)
 {
     // Convert the the $filter parameter into one that makes sense
     // Used when linking BACK to a list
     $filter_array = $this->convert_filter_string_to_array($filter_string);
     $default_values = $this->convert_filter_string_to_array($filter_string);
     // Note that we use this to set default values, not filter the list
     $default_description = $this->describe_filter($default_values);
     $object = $this->get_object_from_ctrl_class_id($ctrl_class_id, $object_id);
     $ctrl_class = CtrlClass::where('id', $ctrl_class_id)->firstOrFail();
     $ctrl_properties = $ctrl_class->ctrl_properties()->where('fieldset', '!=', '')->get();
     $tabbed_form_fields = [];
     $hidden_form_fields = [];
     foreach ($ctrl_properties as $ctrl_property) {
         unset($value);
         // Reset $value , $values
         $values = [];
         // Adjust the field type, mainly to handle relationships and multiple dropdowns
         /* Or, do we actually handle this in the dropdown.blade template? Currently, yes we do:
         			if ($ctrl_property->field_type == 'dropdown' && $ctrl_property->relationship_type == 'belongsToMany') {
         				$ctrl_property->field_type = 'dropdown_multiple';
         			}
         			*/
         // We do use the same view for image and file, though:
         if (in_array($ctrl_property->field_type, ['image', 'file'])) {
             $ctrl_property->template = 'krajee';
         } elseif (in_array($ctrl_property->field_type, ['date', 'datetime'])) {
             $ctrl_property->template = 'date';
         } else {
             $ctrl_property->template = $ctrl_property->field_type;
         }
         if (!view()->exists('ctrl::form_fields.' . $ctrl_property->template)) {
             trigger_error("Cannot load view for field type " . $ctrl_property->field_type);
         }
         // Ascertain the name current value of this field
         // This essentially converts 'one' to 'one_id' and so on
         $field_name = $ctrl_property->get_field_name();
         if ($ctrl_property->related_to_id && in_array($ctrl_property->relationship_type, ['hasMany', 'belongsToMany'])) {
             $related_objects = $object->{$field_name};
             $value = [];
             foreach ($related_objects as $related_object) {
                 $value[$related_object->id] = $this->get_object_title($related_object);
             }
         } else {
             // Do we have a default value set in the querystring?
             if ($default_values && !$object_id) {
                 // We're adding a new object
                 foreach ($default_values as $default_value) {
                     if ($ctrl_property->id == $default_value['ctrl_property_id']) {
                         $value = $default_value['value'];
                     }
                 }
             }
             if (!isset($value)) {
                 // No default value, so pull it from the existing object
                 $value = $object->{$field_name};
             }
         }
         // Do we have a range of valid values for this field? For example, an ENUM or relationship field
         if ($ctrl_property->related_to_id) {
             $related_ctrl_class = \Sevenpointsix\Ctrl\Models\CtrlClass::find($ctrl_property->related_to_id);
             $related_class = $related_ctrl_class->get_class();
             // This breaks as we have too many related items... but we load these via Ajax anyway if there are more than 20. So, just get the first 20...
             // dump($related_class);
             // $related_objects  	= $related_class::all();
             // $related_objects  	= $related_class::take(21)->get();
             // This needs an overhaul, can we chunk for now?
             /* No, doesn't work, times out
             			$related_class::chunk(200, function ($related_objects) {
             			    foreach ($related_objects as $related_object) {
             					$values[$related_object->id] = $this->get_object_title($related_object); 
             				}
             			});
             			*/
             /*
             foreach ($related_objects as $related_object) {
             	$values[$related_object->id] = $this->get_object_title($related_object); 
             }
             */
             // If we use select2 for EVERYTHING (sensible I think?), we can just do this...update template/dropdown accordingly
             if (!empty($value)) {
                 $related_objects = $related_class::where('id', $value)->get();
                 foreach ($related_objects as $related_object) {
                     $values[$related_object->id] = $this->get_object_title($related_object);
                 }
             }
         } else {
             $column = DB::select("SHOW COLUMNS FROM {$ctrl_property->ctrl_class->table_name} WHERE Field = '{$ctrl_property->name}'");
             if (!isset($column[0])) {
                 dump("SHOW COLUMNS FROM {$ctrl_property->ctrl_class->table_name} WHERE Field = '{$ctrl_property->name}'");
                 dd($column);
             }
             $type = $column[0]->Type;
             // Is this an ENUM field?
             preg_match("/enum\\((.*)\\)/", $type, $matches);
             if ($matches) {
                 // Convert 'One','Two','Three' into an array
                 $enums = explode("','", trim($matches[1], "'"));
                 $loop = 1;
                 foreach ($enums as $enum) {
                     // Note that apostrophes are doubled-up when exported from SHOW COLUMNS
                     $value = str_replace("''", "'", $enum);
                     $values[$loop++] = $value;
                 }
             }
         }
         // Build the form_field anddd it to the tabs
         $tab_name = $ctrl_property->fieldset;
         $tab_icon = 'fa fa-list';
         $tab_text = '';
         if (!isset($tabbed_form_fields[$tab_name])) {
             $tabbed_form_fields[$tab_name] = ['icon' => $tab_icon, 'text' => $tab_text, 'form_fields' => []];
         }
         $tabbed_form_fields[$tab_name]['form_fields']['form_id_' . $ctrl_property->name] = ['id' => 'form_id_' . $ctrl_property->name, 'name' => $field_name, 'values' => $values, 'value' => $value, 'type' => $ctrl_property->field_type, 'template' => $ctrl_property->template, 'label' => $ctrl_property->label, 'tip' => $ctrl_property->tip, 'related_ctrl_class_name' => !empty($related_ctrl_class) ? $related_ctrl_class->name : false];
         /*
         	Note: we pass in the related_ctrl_class so that we can use Ajax to generate the list of select2 options.
         	Otherwise, if we're working with (eg) Sogra Products, we have a select box with thousands of options, which breaks.
         */
     }
     // TODO: right, we need to add something here that allows us to customise the list of form fields
     // I think we need to use a serviceprovider and inject it into this main controlller
     // See the comment on this page re. ReportingService: http://stackoverflow.com/questions/30365169/access-controller-method-from-another-controller-in-laravel-5
     if ($this->module->enabled('manipulate_form')) {
         $tabbed_form_fields = $this->module->run('manipulate_form', [$tabbed_form_fields, $ctrl_class_id, $object_id, $filter_string]);
     }
     // Add any filter properties as hidden fields
     if ($default_values) {
         // Set HIDDEN fields here; we can default known fields in the main loop above
         foreach ($default_values as $default_value) {
             $default_property = $ctrl_class->ctrl_properties()->where('fieldset', '')->where('id', $default_value['ctrl_property_id'])->first();
             if ($default_property !== null) {
                 $default_field_name = $default_property->get_field_name();
                 $hidden_form_fields[] = ['id' => 'form_id_' . $default_field_name, 'name' => $default_field_name, 'value' => $default_value['value']];
             }
         }
     }
     if ($object_id) {
         $page_title = 'Edit this ' . $ctrl_class->get_singular();
         // $page_description = '“'.$object->title.'”';
         $page_description = '“' . $this->get_object_title($object) . '”';
         $delete_link = route('ctrl::delete_object', [$ctrl_class->id, $object->id]);
     } else {
         $page_title = 'Add ' . $this->a_an($ctrl_class->get_singular()) . ' ' . $ctrl_class->get_singular();
         $page_description = $default_description ? '…' . $default_description : '';
         $delete_link = '';
     }
     // If we've set default values here, then that implies that we came through a filtered list; and we want to go back to THAT list, not a list of all these items
     // Or do we...? Hmmm. It might make more sense to return to a filtered list of *these* items... TBC.
     /* Yes, use the code below from @list_objects
     		$back_link        = route('ctrl::list_objects',[$ctrl_class->id,$filter_string]);
     		*/
     // Do we have an unfiltered list we can link back to?
     if ($filter_array) {
         // dd($filter_array);
         // $filter_array[0]['ctrl_property_id'] is now the ID of the property that links back to the "parent" list, so:
         $unfiltered_ctrl_property = CtrlProperty::where('id', $filter_array[0]['ctrl_property_id'])->firstOrFail();
         $unfiltered_ctrl_class = CtrlClass::where('id', $unfiltered_ctrl_property->related_to_id)->firstOrFail();
         $back_link = route('ctrl::list_objects', [$unfiltered_ctrl_class->id]);
     } else {
         // Is this a sensible fallback?
         $back_link = route('ctrl::list_objects', [$ctrl_class->id, $filter_string]);
     }
     // Similarly... once we've saved a filtered object, we want to bounce back to a filtered list. This enables it:
     $save_link = route('ctrl::save_object', [$ctrl_class->id, $object_id, $filter_string]);
     return view('ctrl::edit_object', ['ctrl_class' => $ctrl_class, 'page_title' => $page_title, 'page_description' => $page_description, 'back_link' => $back_link, 'delete_link' => $delete_link, 'save_link' => $save_link, 'object' => $object, 'tabbed_form_fields' => $tabbed_form_fields, 'hidden_form_fields' => $hidden_form_fields]);
 }
Esempio n. 2
0
 /**
  * Generate model files based on the ctrl_tables
  *
  * @return Response
  */
 public function generate_model_files()
 {
     $model_folder = 'Ctrl/Models/';
     if (!File::exists(app_path($model_folder))) {
         File::makeDirectory(app_path($model_folder), 0777, true);
         // See http://laravel-recipes.com/recipes/147/creating-a-directory
     } else {
         // Otherwise, empty the folder:
         File::cleanDirectory(app_path($model_folder));
     }
     $ctrl_classes = \Sevenpointsix\Ctrl\Models\CtrlClass::get();
     foreach ($ctrl_classes as $ctrl_class) {
         $view_data = ['model_name' => $ctrl_class->name, 'soft_deletes' => false, 'table_name' => $ctrl_class->table_name, 'fillable' => [], 'belongsTo' => [], 'hasMany' => [], 'belongsToMany' => []];
         // NOTE: this may need to include properties that we set using a filter in the URL
         // ie, if we want to add a course to a client, but "client" isn't directly visible in the form;
         // instead, we get to the list of courses by clicking the filtered_list "courses" when listing clients.
         $fillable_properties = $ctrl_class->ctrl_properties()->where('fieldset', '!=', '')->where(function ($query) {
             $query->whereNull('relationship_type')->orWhere('relationship_type', 'belongsTo');
         })->get();
         // We can only fill relationships if they're belongsTo (ie, have a specific local key, such as one_id)
         // OR if they're belongsToMany, in which case we have a pivot table (I think?)
         foreach ($fillable_properties as $fillable_property) {
             $view_data['fillable'][] = $fillable_property->get_field_name();
             // Does Laravel/Eloquent give us a quick way of extracting all ->name properties into an array?
             // I think it does.
         }
         // Which properties can be automatically filled via a filtered list? ie, clicking to add a related page to a pagesection, should set the pagesection variable.
         // This is a bit complex as we have to look at properties of other classes, linking to this class...
         $filtered_list_properties = \Sevenpointsix\Ctrl\Models\CtrlProperty::whereRaw('(find_in_set(?, flags))', ['filtered_list'])->where('related_to_id', $ctrl_class->id)->get();
         if (!$filtered_list_properties->isEmpty()) {
             foreach ($filtered_list_properties as $filtered_list_property) {
                 $default_properties = $ctrl_class->ctrl_properties()->where('relationship_type', 'belongsTo')->where('related_to_id', $filtered_list_property->ctrl_class_id)->get();
                 if (!$default_properties->isEmpty()) {
                     foreach ($default_properties as $default_property) {
                         $view_data['fillable'][] = $default_property->get_field_name();
                     }
                 }
             }
         }
         $relationship_properties = $ctrl_class->ctrl_properties()->whereNotNull('related_to_id')->get();
         foreach ($relationship_properties as $relationship_property) {
             $related_ctrl_class = \Sevenpointsix\Ctrl\Models\CtrlClass::find($relationship_property->related_to_id);
             $relationship_data = ['name' => $relationship_property->name, 'model' => $related_ctrl_class->name, 'foreign_key' => $relationship_property->foreign_key, 'local_key' => $relationship_property->local_key];
             if ($relationship_property->relationship_type == 'belongsToMany') {
                 $relationship_data['pivot_table'] = $relationship_property->pivot_table;
             }
             $view_data[$relationship_property->relationship_type][] = $relationship_data;
         }
         $model_code = View::make('ctrl::model_template', $view_data)->render();
         $model_path = app_path($model_folder . $ctrl_class->name . '.php');
         File::put($model_path, $model_code);
     }
     $this->info($ctrl_classes->count() . ' files generated');
 }