Example #1
0
 /**
  * Update an object a given CtrlClass, if an ID is given
  * Or create a new object if not
  * @param  Request  $request
  * @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 This tracks whether we're adding a filtered object -- so we can bounce back to the filtered list..
  *
  * @return Response
  */
 public function save_object(Request $request, $ctrl_class_id, $object_id = NULL, $filter_string = NULL)
 {
     $ctrl_class = CtrlClass::where('id', $ctrl_class_id)->firstOrFail();
     $ctrl_properties = $ctrl_class->ctrl_properties()->where('fieldset', '!=', '')->get();
     // Validate the post:
     $validation = [];
     foreach ($ctrl_properties as $ctrl_property) {
         $field_name = $ctrl_property->get_field_name();
         $flags = explode(',', $ctrl_property->flags);
         if (in_array('required', $flags)) {
             $validation[$field_name][] = 'required';
         }
         // Note: could also do this in query builder:
         /*
         	$required_properties = $ctrl_class->ctrl_properties()
         		->whereRaw("FIND_IN_SET('required',flags) > 0")
         		->get();  	
         */
         if ($ctrl_property->field_type == 'email') {
             $validation[$field_name][] = 'email';
         }
         // Check for valid dates; not sure if tis is correct?
         if (in_array($ctrl_property->field_type, ['date', 'datetime'])) {
             $validation[$field_name][] = 'date';
         }
         if (!empty($validation[$field_name])) {
             $validation[$field_name] = implode('|', $validation[$field_name]);
         }
     }
     if ($validation) {
         $this->validate($request, $validation);
     }
     $class = $ctrl_class->get_class();
     $object = $object_id ? $class::where('id', $object_id)->firstOrFail() : new $class();
     // Convert dates back into MySQL format; this feels quite messy but I can't see where else to do it:
     foreach ($ctrl_properties as $ctrl_property) {
         if (in_array($ctrl_property->field_type, ['date', 'datetime']) && !empty($_POST[$ctrl_property->name])) {
             $date_format = $ctrl_property->field_type == 'date' ? 'Y-m-d' : 'Y-m-d H:i:s';
             $_POST[$ctrl_property->name] = date($date_format, strtotime($_POST[$ctrl_property->name]));
         }
     }
     $object->fill($_POST);
     $object->save();
     // Save the new object, otherwise we can't save any relationships...
     // Now load any related fields (excluding belongsTo, as this indicates the presence of an _id field)
     $related_ctrl_properties = $ctrl_class->ctrl_properties()->where('fieldset', '!=', '')->where(function ($query) {
         $query->where('relationship_type', 'hasMany')->orWhere('relationship_type', 'belongsToMany');
     })->get();
     foreach ($related_ctrl_properties as $related_ctrl_property) {
         $related_field_name = $related_ctrl_property->get_field_name();
         if ($request->input($related_field_name)) {
             // $request->input($related_field_name) is always an array here, I think?
             $related_ctrl_class = \Sevenpointsix\Ctrl\Models\CtrlClass::find($related_ctrl_property->related_to_id);
             // NOTE: we need some standard functions for the following:
             /*
             	            		- Loading the object for a class
             	            		- Loading the related class from a property
             	            		- Loading related properties for a class
             	            		... etc
             */
             $related_class = $related_ctrl_class->get_class();
             $related_objects = $related_class::find($request->input($related_field_name));
             if ($related_ctrl_property->relationship_type == 'hasMany' || $related_ctrl_property->relationship_type == 'belongsToMany') {
                 // OK, I think we can use synch here; or does this break for hasMany?
                 // Yeah, breaks for hasMany... :-(  This works though:
                 if ($related_ctrl_property->relationship_type == 'belongsToMany') {
                     $object->{$related_field_name}()->sync($related_objects);
                 } else {
                     if ($related_ctrl_property->relationship_type == 'hasMany') {
                         $object->{$related_field_name}()->saveMany($related_objects);
                     }
                 }
                 /*
                 // A hasMany relationship needs saveMany
                 // belongsToMany might need attach, or synch -- TBC
                 
                 
                 	            	
                 	            	dump ("Loading existing_related_objects with $related_field_name");
                 		          	$existing_related_objects = $object->$related_field_name();
                 		          	$inverse_property = CtrlProperty::where('ctrl_class_id',$related_ctrl_class->id)
                 								  ->where('foreign_key',$related_ctrl_property->foreign_key)
                 								  ->first(); // Does this always hold true?
                 					$inverse_field_name = $inverse_property->name;
                 
                 		          	foreach ($existing_related_objects as $existing_related_object) {	
                 		          		dump('$existing_related_object'. $existing_related_object);
                 		          		$existing_related_object->$inverse_field_name()->dissociate();
                 		          		dump("Removing relationship to $related_field_name");
                 		          		$existing_related_object->save(); 
                 		          			// This seems unnecessarily complicated; review this.
                 		          			// Is there no equivalent of synch() for hasMany/belongsTo relationships?
                 		          			// Something like, $object->related_field_name()->sync($related_objects);
                 		          			// That doesn't work though...
                 		          	}
                 		          	// dd($related_objects->toArray());
                 		          	dump("Saving $related_field_name");
                 		          	// dump($related_objects->lists('id'));
                 		          	foreach ($related_objects as $r) {
                 		          		// dump("Saving $related_field_name with ID $r->id to object ".get_class($object));
                 		          		// $object->$related_field_name()->save($r);
                 		          	}
                 		          	// Why wouldn't this work?
                 		          	// $object->$related_field_name()->sync($related_objects);
                 		          	// Gives same error as below:
                 // $object->$related_field_name()->saveMany($related_objects);
                 
                 		          	// Maybe...
                 
                 		          	if ($related_ctrl_property->relationship_type == 'hasMany') {
                 		          		$object->$related_field_name()->saveMany($related_objects);
                 		          	}
                 	            	else if ($related_ctrl_property->relationship_type == 'belongsToMany') {
                 
                 	            		// $object->$related_field_name()->attach($related_objects);
                 	            		foreach      ($related_objects as $r) {
                 		          			dump("Saving $related_field_name with ID $r->id to object ".get_class($object));
                 			          		$object->$related_field_name()->attach($r->id);
                 			          	}
                 	            	}
                 //$object->save();
                 // This is ALMOST working but glitches; we seem to save the relationship then overwrite it when we try to remove it, even though we try to remove it first. Do we need to lock the tables here?
                 */
             }
         }
     }
     $object->save();
     // Add a custom post_save module
     if ($this->module->enabled('post_save')) {
         // We may eventually need to patch this into the validation...? Or would that imply the need for a validation (or pre_save) module?
         $this->module->run('post_save', [$request, $object, $filter_string]);
     }
     $redirect = route('ctrl::list_objects', [$ctrl_class->id, $filter_string]);
     if ($request->ajax()) {
         return json_encode(['redirect' => $redirect]);
     } else {
         return redirect($redirect);
     }
 }
Example #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');
 }