diff --git a/app/Http/Controllers/RecipeController.php b/app/Http/Controllers/RecipeController.php index 34b7a1f..0039f4e 100644 --- a/app/Http/Controllers/RecipeController.php +++ b/app/Http/Controllers/RecipeController.php @@ -37,6 +37,118 @@ class RecipeController extends Controller { $recipe = Recipe::with('ingredients')->with('categories')->findOrFail($id); + $this->validate($request, [ + 'name'=>'required|max:500', + 'author'=>'required|max:500', + 'user_id'=>'required', + 'servings'=>'required|integer', + 'serving_size'=>'required|max:40', + 'description'=>'max:5000', + 'instructions'=>'required|max:5000' + ]); + + //break categories and ingredients into arrays for validation + $categories = array(); + $ingredients = array(); + foreach($request->all() as $element => $value){ + if(preg_match('/category_.*/', $element)){ + $explosion = explode('_',$element); + $categories[$explosion[1]][$explosion[2]]=$value; + }else if(preg_match('/ingredient_.*/', $element)){ + $explosion = explode('_',$element); + $ingredients[$explosion[1]][$explosion[2]]=$value; + } + } + + foreach($categories as $category){ + $newreq = new Request($category); + $this->validate($newreq, [ + 'name'=>'required|max:50', + ]); + } + + foreach($ingredients as $index => $ingredient){ + $newreq = new Request($ingredient); + $this->validate($newreq, [ + 'order'=>'required|integer', + 'quantity'=>['required','regex:/(^\d+\/\d+$)|(^\d*\.\d+$)|(^\d+ \d\/\d$)|(^\d+$)/i'], + 'measurement'=>'required|max:40', + 'name'=>'required|max:500', + 'notes'=>'max:500' + ]); + if ($ingredient['notes'] == null){ + $ingredients[$index]['special_notes'] = ''; + }else{ + $ingredients[$index]['special_notes'] = $ingredient['notes']; + } + if(array_key_exists('alternative', $ingredient) && $ingredient['alternative'] == 'on'){ + $ingredients[$index]['alternative'] = true; + }else{ + $ingredients[$index]['alternative'] = false; + } + } + + $update = $request->only('name','author','user_id','servings','serving_size','last_modified','description','instructions'); + $update['last_modified']=now(); + $recipe->fill($update)->save(); + + //Delete categories that don't exist in new category array + foreach($recipe->categories as $category){ + if(!array_key_exists($category->id,$categories)){ + $category->delete(); + } + } + + //Delete ingredients that don't exist in new ingredient array + foreach($recipe->ingredients as $ingredient){ + if(!array_key_exists($ingredient->id,$ingredients)){ + $ingredient->delete(); + } + } + + //create/update categories + foreach($categories as $categoryid => $category){ + //Add recipe_id + $category["recipe_id"]=$id; + $newreq = new Request($category); + //Find category if exists, create if it doesn't + $currentcategory = RecipeCategory::where('id','=',$categoryid)->first(); + if($currentcategory === null){ + RecipeCategory::create($newreq->only('recipe_id','name')); + }else{ + $update = $newreq->only('recipe_id','name'); + $currentcategory->fill($update)->save(); + } + } + + //create/update ingredients + foreach($ingredients as $ingredientid => $ingredient){ + //Add recipe_id + $ingredient["recipe_id"]=$id; + $newreq = new Request($ingredient); + //Find category if exists, create if it doesn't + $currentingredient = RecipeIngredient::where('id','=',$ingredientid)->first(); + if($currentingredient === null){ + RecipeIngredient::create($newreq->only('recipe_id','order','alternative','quantity','measurement','name','special_notes')); + }else{ + $update = $newreq->only('recipe_id','order','alternative','quantity','measurement','name','special_notes'); + $currentingredient->fill($update)->save(); + } + } + return redirect()->route('recipes.index')->with('message','Recipe successfully edited.'); + } + + public function create(){ + $lists['ingredients']=array_values(RecipeIngredient::get()->sortby('name')->pluck('name')->unique()->toArray()); + $lists['measurements']=array_values(RecipeIngredient::get()->sortby('measurement')->pluck('measurement')->unique()->toArray()); + $lists['categories']=array_values(RecipeCategory::get()->sortby('name')->pluck('name')->unique()->toArray()); + $lists['authors']=array_values(Recipe::get()->sortby('author')->pluck('author')->unique()->toArray()); + $lists['owners']=User::select('id','name')->get()->sortby('name'); + return view('recipes.create')->with('lists',$lists); + } + + public function store(Request $request){ + $this->validate($request, [ 'author'=>'required|max:500', 'user_id'=>'required', @@ -87,51 +199,25 @@ class RecipeController extends Controller } } - $update = $request->only('author','user_id','servings','serving_size','description','instructions'); - $recipe->fill($update)->save(); + $create=$request->only('author','user_id','servings','serving_size','name','description','instructions'); + $create['date_entered']=now(); + $create['date_modified']=now(); + $recipe = Recipe::create($create); - //Delete categories that don't exist in new category array - foreach($recipe->categories as $category){ - if(!array_key_exists($category->id,$categories)){ - $category->delete(); - } - } - - //Delete ingredients that don't exist in new ingredient array - foreach($recipe->ingredients as $ingredient){ - if(!array_key_exists($ingredient->id,$ingredients)){ - $ingredient->delete(); - } - } - - //create/update categories + //create categories foreach($categories as $categoryid => $category){ //Add recipe_id - $category["recipe_id"]=$id; + $category["recipe_id"]=$recipe->id; $newreq = new Request($category); - //Find category if exists, create if it doesn't - $currentcategory = RecipeCategory::where('id','=',$categoryid)->first(); - if($currentcategory === null){ - RecipeCategory::create($newreq->only('recipe_id','name')); - }else{ - $update = $newreq->only('recipe_id','name'); - $currentcategory->fill($update)->save(); - } + RecipeCategory::create($newreq->only('recipe_id','name')); } - //create/update ingredients + //create ingredients foreach($ingredients as $ingredientid => $ingredient){ //Add recipe_id - $ingredient["recipe_id"]=$id; + $ingredient["recipe_id"]=$recipe->id; $newreq = new Request($ingredient); - //Find category if exists, create if it doesn't - $currentingredient = RecipeIngredient::where('id','=',$ingredientid)->first(); - if($currentingredient === null){ - RecipeIngredient::create($newreq->only('recipe_id','order','alternative','quantity','measurement','name','special_notes')); - }else{ - $update = $newreq->only('recipe_id','order','alternative','quantity','measurement','name','special_notes'); - $currentingredient->fill($update)->save(); - } + RecipeIngredient::create($newreq->only('recipe_id','order','alternative','quantity','measurement','name','special_notes')); } return redirect()->route('recipes.index')->with('message','Recipe successfully edited.'); } diff --git a/resources/views/recipes/create.blade.php b/resources/views/recipes/create.blade.php new file mode 100644 index 0000000..cd41ca1 --- /dev/null +++ b/resources/views/recipes/create.blade.php @@ -0,0 +1,200 @@ +@extends('layouts.default') +@extends('content_wrappers.md-10') + +@section('title', ' | Create Recipe') +@section('heading', 'Create Recipe') + +@section('content') +<div class="panel-body"> + {{ Form::model(null, array('route' => array('recipes.store'), 'method' => 'POST', 'id' => 'recipeForm')) }} + <div class="row"> + <div class="row"> + <div class="form-group col-md-offset-3 col-md-6 text-center"> + {{ Form::label('name', 'Recipe Name:') }} + {{ Form::text('name', null, array('class' => 'form-control', 'id' => 'name')) }} + </div> + </div> + <div class="form-group col-md-3 text-center"> + {{ Form::label('author', 'Created By:') }} + {{ Form::text('author', null, array('class' => 'form-control', 'id' => 'authorFilter')) }} + </div> + <div class="col-md-3 text-center"> + {{ Form::label('user_id', 'Maintained By:') }} + <select name="user_id" class="form-control" id="user"> + @foreach($lists['owners'] as $user) + <option value="{{ $user->id}}" {{ Auth::user()->id == $user->id ? 'selected' : '' }}> {{ $user->name }}</option> + @endforeach + </select> + </div> + + <div class="col-md-3 text-center">Entered On: {{now()->format('Y/m/d') }}</div> + <div class="col-md-3 text-center">Last Changed: {{now()->format('Y/m/d') }}</div> + </div> + <div class="row"> + <div class="col-sm-offset-3 col-sm-3 text-center"> + {{ Form::label('servings', 'Servings:') }} + {{ Form::text('servings', null, array('class' => 'form-control','id' => 'servings')) }} + </div> + <div class="col-sm-3 text-center"> + {{ Form::label('servings', 'Serving Size:') }} + {{ Form::text('serving_size', null, array('class' => 'form-control','id' => 'serving_size')) }} + </div> + </div> + <br /> + <div class="row"><div class="panel-heading"><h1>Categories</h1></div></div> + <div id="categories" class="row text-center"> + </div> + <div class='row text-center'> + <div class='col-sm-offset-4 col-sm-2'><input id="categoryFilter" class="filter form-control" type="text" placeholder="Search.."></div> + <div class='col-sm-2'>{{ Form::button("Add", array('id'=>'addCategory','class'=>'form-control'))}}</div> + </div> + <br /> + <div class="row"><div class="panel-heading"><h1>Description</h1></div></div> + {{ Form::textarea('description', null, array('class' => 'form-control', 'id' => 'descriptionEditor')) }} + <br /> + <div class="row"><div class="panel-heading"><h1>Ingredients</h1></div></div> + <div class="table-responsive"> + <table class="table table-bordered table-striped" id="ingredientTable"> + <thead> + <tr> + <th class='col-sm-1'>Order</th> + <th class='col-sm-2'>Alternative?</th> + <th class='col-sm-1'>Quantity</th> + <th class='col-sm-2'>Measurement</th> + <th class='col-sm-5'>Name          </th> + <th class='col-sm-5'>Notes</th> + <th class='col-sm-2'></th> + </tr> + </thead> + <tr id="blankRow"><td colspan='7'></td></tr> + <tr id="addRow"> + <td></td> + <td>{{Form::checkbox(null,null,false,array('class' => 'form-control ingredientAlternative','id'=>'addIngredientAlternative'))}}</td> + <td>{{Form::text(null,null,array('class' => 'form-control ingredientQuantity'))}}</td> + <td>{{Form::text(null,null,array('class' => 'form-control ingredientMeasurement'))}}</td> + <td>{{Form::text(null,null,array('class' => 'form-control ingredientName'))}}</td> + <td>{{Form::text(null,null,array('class' => 'form-control ingredientNotes'))}}</td> + <td>{{Form::button("Add",array('class' => 'form-control','id' => 'addIngredient'))}}</td> + </tr> + </table> + </div> + + <br /><br /> + <div class="row"><div class="panel-heading"><h1>Instructions</h1></div></div> + {{ Form::textarea('instructions', null, array('class' => 'form-control', 'id' => 'instructionsEditor')) }} +</div> + +{{ Form::submit('Create Recipe', array('class' => 'btn btn-primary','id'=>'submit')) }} + +{{ Form::close() }} +@endsection + +@section('scripts') + <script src="https://cdn.ckeditor.com/4.17.2/standard-all/ckeditor.js"></script> + <script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> + <script> + CKEDITOR.replace('instructionsEditor'); + CKEDITOR.replace('descriptionEditor'); + + var authors = @json($lists['authors']); + $("#authorFilter").autocomplete({ + source: authors + }); + var categories = @json($lists['categories']); + $("#categoryFilter").autocomplete({ + source: categories + }); + + var ingredients = @json($lists['ingredients']); + $(".ingredientName").autocomplete({ + source: ingredients + }); + + var measurements = @json($lists['measurements']); + $(".ingredientMeasurement").autocomplete({ + source: measurements + }); + + $("#categories").on("click",'.category_button',function(){ + $(this).parent().remove(); + }); + + $("#addCategory").on("click", function(){ + var newCategory = upperWords($('#categoryFilter').val().toLowerCase()); + var currentCategories = []; + $(".category").each(function(){ + currentCategories.push($(this).text()); + }); + if(newCategory!="" && currentCategories.indexOf(newCategory) == -1){ + var id=uuidv4(); + $("#categories").append('<input name="category_'+id+'_name" type="hidden" class="form-control category_name ui-autocomplete-input" autocomplete="off" value="'+newCategory+'"><button type="button" class="btn form-control category_button" style="width: 100px; margin-left: 10px;margin-bottom: 10px;">'+newCategory+'</button>'); + } + }); + + function upperWords(str){ + words=str.split(" "); + words.forEach(function(word,i){ + words[i]=word[0].toUpperCase() + word.slice(1,word.length); + }); + return words.join(" "); + } + + $("#ingredientTable").on("click",".ingredientRemove",function(){ + $(this).parent().parent().remove(); + $("#ingredientTable").find('.recipeIngredient .ingredientAlternative:first').attr('disabled',true); + $("#ingredientTable").find('.recipeIngredient .ingredientAlternative:first').attr('checked',false); + }); + + $("#addIngredient").on("click", function(){ + var ingredientAlternative=$('#addIngredientAlternative').prop("checked"); + var ingredientQuantity=$('#addRow .ingredientQuantity').val(); + var ingredientMeasurement=$('#addRow .ingredientMeasurement').val(); + var ingredientName=$('#addRow .ingredientName').val(); + var ingredientNotes=$('#addRow .ingredientNotes').val(); + if(isFraction(ingredientQuantity) && ingredientMeasurement != "" && ingredientName != ""){ + var id=uuidv4(); + var newRow='<tr class="recipeIngredient">'; + newRow+='<td><input name="ingredient_'+id+'_order" type="hidden" class="form-control ingredientOrder ui-autocomplete-input" autocomplete="off"></td>'; + newRow+='<td><input name="ingredient_'+id+'_alternative" type="checkbox" '+ (ingredientAlternative ? 'checked="checked"' : "") +'class="form-control ingredientAlternative"></td>'; + newRow+='<td><input name="ingredient_'+id+'_quantity" type="text" value="'+ingredientQuantity+'" class="form-control ingredientQuantity"></td>'; + newRow+='<td><input name="ingredient_'+id+'_measurement" type="text" value="'+ingredientMeasurement+'" class="form-control ingredientMeasurement ui-autocomplete-input" autocomplete="off"></td>'; + newRow+='<td><input name="ingredient_'+id+'_name" type="text" value="'+ingredientName+'" class="form-control ingredientName ui-autocomplete-input" autocomplete="off"></td>'; + newRow+='<td><input name="ingredient_'+id+'_notes" type="text" value="'+ingredientNotes+'" class="form-control ingredientNotes ui-autocomplete-input" autocomplete="off"></td>'; + newRow+='<td><button type="button" class="form-control ingredientRemove">Remove</button></td>'; + newRow+='</tr>'; + $("#blankRow").before(newRow); + $("#ingredientTable").find('.recipeIngredient .ingredientAlternative:first').attr('disabled',true); + $("#ingredientTable").find('.recipeIngredient .ingredientAlternative:first').attr('checked',false); + }else{ + if(ingredientMeasurement == ""){alert("Ingredient measurement required")}; + if(ingredientName == ""){alert("Ingredient name required")}; + } + }); + + function isFraction(value){ + var pattern = new RegExp(/(^\d+\/\d+$)|(^\d*\.\d+$)|(^\d+ \d\/\d$)|(^\d+$)/); + if(value.match(pattern)){return true} + else{ + alert("Ingredient quantity must be a decimal or fraction"); + return false + } + } + $('#submit').on("click",function(){ + $('.recipeIngredient').each(function(index){ + $(this).find('.ingredientOrder').val(index); + }); + $('#recipeForm').submit(); + }); + + function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + </script> +@endsection + +@section('styles') + <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> +@endsection diff --git a/resources/views/recipes/edit.blade.php b/resources/views/recipes/edit.blade.php index d7a34a7..0a699be 100644 --- a/resources/views/recipes/edit.blade.php +++ b/resources/views/recipes/edit.blade.php @@ -2,11 +2,17 @@ @extends('content_wrappers.md-10') @section('title', ' | Edit '.$recipe->name) -@section('heading', 'Edit '.$recipe->name) +@section('heading', 'Edit Recipe: '.$recipe->name) @section('content') <div class="panel-body"> {{ Form::model($recipe, array('route' => array('recipes.update', $recipe->id), 'method' => 'PUT', 'id' => 'recipeForm')) }} + <div class="row"> + <div class="form-group col-md-offset-3 col-md-6 text-center"> + {{ Form::label('name', 'Recipe Name:') }} + {{ Form::text('name', null, array('class' => 'form-control', 'id' => 'name')) }} + </div> + </div> <div class="row"> <div class="form-group col-md-3 text-center"> {{ Form::label('author', 'Created By:') }} @@ -20,7 +26,6 @@ @endforeach </select> </div> - <div class="col-md-3 text-center">Entered On: {{$recipe->date_entered->format('Y/m/d') }}</div> <div class="col-md-3 text-center">Last Changed: {{$recipe->date_modified->format('Y/m/d') }}</div> </div> @@ -86,7 +91,7 @@ <tr id="blankRow"><td colspan='7'></td></tr> <tr id="addRow"> <td></td> - <td>{{Form::checkbox($ingredient->alternative,null,false,array('class' => 'form-control ingredientAlternative','id'=>'addIngredientAlternative'))}}</td> + <td>{{Form::checkbox(null,null,false,array('class' => 'form-control ingredientAlternative','id'=>'addIngredientAlternative'))}}</td> <td>{{Form::text(null,null,array('class' => 'form-control ingredientQuantity'))}}</td> <td>{{Form::text(null,null,array('class' => 'form-control ingredientMeasurement'))}}</td> <td>{{Form::text(null,null,array('class' => 'form-control ingredientName'))}}</td> @@ -101,7 +106,7 @@ {{ Form::textarea('instructions', $recipe->instructions, array('class' => 'form-control', 'id' => 'instructionsEditor')) }} </div> -{{ Form::submit('Save', array('class' => 'btn btn-primary','id'=>'submit')) }} +{{ Form::submit('Update Recipe', array('class' => 'btn btn-primary','id'=>'submit')) }} {{ Form::close() }} @endsection