I have a settings table where I store things like website title, social network links and other things... I make then all acessible by seting a cache variable.
Now, my question is, how can I update this table? By example... If I have the following blade form:
{!! Form::model(config('settings'), ['class' => 's-form', 'route' => ['setting.update']]) !!}
{{ method_field('PUT') }}
<div class="s-form-item text">
<div class="item-title required">Nome do site</div>
{!! Form::text('title', null, ['placeholder' => 'Nome do site']) !!}
</div>
<div class="s-form-item text">
<div class="item-title required">Descrição do site</div>
{!! Form::text('desc', null, ['placeholder' => 'Descrição do site']) !!}
</div>
<div class="s-form-item s-btn-group s-btns-right">
Voltar
<input class="s-btn" type="submit" value="Atualizar">
</div>
{!! Form::close() !!}
In the PUT request how can I search in the table by the each name passed and update the table? Here are the another files:
Route
Route::put('/', ['as' => 'setting.update', 'uses' => 'Admin\AdminConfiguracoesController#update']);
Controller
class AdminConfiguracoesController extends AdminBaseController
{
private $repository;
public function __construct(SettingRepository $repository){
$this->repository = $repository;
}
public function geral()
{
return view('admin.pages.admin.configuracoes.geral.index');
}
public function social()
{
return view('admin.pages.admin.configuracoes.social.index');
}
public function analytics()
{
return view('admin.pages.admin.configuracoes.analytics.index');
}
public function update($id, Factory $cache, Setting $setting)
{
// Update?
$cache->forget('settings');
return redirect('admin');
}
}
Repository
class SettingRepository
{
private $model;
public function __construct(Setting $model)
{
$this->model = $model;
}
public function findByName($name){
return $this->model->where('name', $name);
}
}
Model
class Setting extends Model
{
protected $table = 'settings';
public $timestamps = false;
protected $fillable = ['value'];
}
ServiceProvider
class SettingsServiceProvider extends ServiceProvider
{
public function boot(Factory $cache, Setting $settings)
{
$settings = $cache->remember('settings', 60, function() use ($settings)
{
return $settings->lists('value', 'name')->all();
});
config()->set('settings', $settings);
}
public function register()
{
//
}
}
Migration
class CreateSettingsTable extends Migration
{
public function up()
{
Schema::create('settings', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100)->unique();
$table->text('value');
});
}
public function down()
{
Schema::drop('settings');
}
}
Ok, step by step.
First, let's think about what we really want to achieve and look at the implementation in the second step.
Looking at your code, I assume that you want to create an undefined set of views that contain a form for updating certain settings. For the user, the settings seem to be structured in groups, e.g. "General", "Social", "Analytics", but you don't structure your settings in the database like that. Your settings is basically a simple key/value-store without any relation to some settings group.
When updating, you want a single update method that handles all settings, disregarding which form the update request is sent from.
I hope I'm correct with my assumptions, correct me if I'm not.
Okay cool, but, come on, how do I implement that?
As always, there are probably a thousand ways you can implement something like this. I've written a sample application in order to explain how I would implement it, and I think it's pretty Laravelish (what a word!).
1. How should my data be stored?
We already talked about it. We want a basic key/value-store that persists in the database. And because we work with Laravel, let's create a model and a migration for that:
php artisan make:model Setting --migration
This will create a model and the appropriate migration. Let's edit the migration to create our key/value columns:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSettingsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('settings', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->text('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('settings');
}
}
In the Setting model, we have to add the name column to the fillable array. I'll explain why we need to below. Basically, we want to use some nice Laravel APIs and therefore we have to make the name-attribute fillable.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model {
/**
* #var array
*/
protected $fillable = ['name'];
}
2. How do I want to access the settings data?
We discussed this in your last question, so I won't go into detail about this and I pretend that this code already exists. I'll use a repository in this example, so I will update the SettingsServiceProvider during development.
3. Creating the repositories
To make the dependencies more loosely coupled, I will create an Interface (Contract in the Laravel world) and bind it to a concrete implementation. I can then use the contract with dependency injection and Laravel will automatically resolve the concrete implementation with the Service Container. Maybe this is overkill for your app, but I love writing testable code, no matter how big my application will be.
app/Repositories/SettingRepositoryInterface.php:
<?php
namespace App\Repositories;
interface SettingRepositoryInterface {
/**
* Update a setting or a given set of settings.
*
* #param string|array $key
* #param string $value
*
* #return void
*/
public function update($key, $value);
/**
* List all available settings (name => value).
*
* #return array
*/
public function lists();
}
As you can see, we will use the repository for updating settings and listing our settings in a key/value-array.
The concrete implementation (for Eloquent in this example) looks like this:
app/Repositories/EloquentSettingRepository.php
<?php
namespace App\Repositories;
use App\Setting;
class EloquentSettingRepository implements SettingRepositoryInterface {
/**
* #var \App\Setting
*/
private $settings;
/**
* EloquentSettingRepository constructor.
*
* #param \App\Setting $settings
*/
public function __construct(Setting $settings)
{
$this->settings = $settings;
}
/**
* Update a setting or a given set of settings.
* If the first parameter is an array, the second parameter will be ignored
* and the method calls itself recursively over each array item.
*
* #param string|array $key
* #param string $value
*
* #return void
*/
public function update($key, $value = null)
{
if (is_array($key))
{
foreach ($key as $name => $value)
{
$this->update($name, $value);
}
return;
}
$setting = $this->settings->firstOrNew(['name' => $key]);
$setting->value = $value;
$setting->save();
}
/**
* List all available settings (name => value).
*
* #return array
*/
public function lists()
{
return $this->settings->lists('value', 'name')->all();
}
}
The DocBlocks should pretty much explain how the repository is implemented. In the update method, we make use of the firstOrNew method. Thats why we had to update the fillable-array in our model.
Now let's bind the interface to that implementation. In app/Providers/SettingsServiceProvider.php, add this to the register-method:
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
\App\Repositories\SettingRepositoryInterface::class,
\App\Repositories\EloquentSettingRepository::class
);
}
We could have added this to the AppServiceProvider, but since we have a dedicated service provider for our settings we will use it for our binding.
Now that we have finished the repository, we can update the existing code in the boot-method of our SettingsServiceProvider so that it uses the repository instead of hardcoding App\Setting.
/**
* Bootstrap the application services.
*
* #param \Illuminate\Contracts\Cache\Factory $cache
* #param \App\Repositories\SettingRepositoryInterface $settings
*/
public function boot(Factory $cache, SettingRepositoryInterface $settings)
{
$settings = $cache->remember('settings', 60, function() use ($settings)
{
return $settings->lists();
});
config()->set('settings', $settings);
}
4. Routes and controller
In this simple example, the homepage will show a form to update some settings. Making a PUT/PATCH-request on the same route will trigger the update method:
<?php
get('/', ['as' => 'settings.index', 'uses' => 'Admin\SettingsController#index']);
put('/', ['as' => 'settings.update', 'uses' => 'Admin\SettingsController#update']);
The index-method of our controller will return a view that contains the form. I've commented the update method throughout to explain what each line does:
app/Http/Controllers/Admin/SettingsController.php:
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Contracts\Cache\Factory;
use Illuminate\Http\Request;
use App\Repositories\SettingRepositoryInterface;
class SettingsController extends AdminBaseController {
/**
* #var \App\Repositories\SettingRepositoryInterface
*/
private $settings;
/**
* SettingsController constructor.
*
* #param \App\Repositories\SettingRepositoryInterface $settings
*/
public function __construct(SettingRepositoryInterface $settings)
{
$this->settings = $settings;
}
/**
* Shows the setting edit form.
*
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
{
return view('settings.index');
}
/**
* Update the settings passed in the request.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Contracts\Cache\Factory $cache
*
* #return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, Factory $cache)
{
// This will get all settings as a key/value array from the request.
$settings = $request->except('_method', '_token');
// Call the update method on the repository.
$this->settings->update($settings);
// Clear the cache.
$cache->forget('settings');
// Redirect to some page.
return redirect()->route('settings.index')
->with('updated', true);
}
}
Note the first statement in the update method. It will fetch all POST-data from the request, except the method and CSRF token. $settings is now an associative array with your settings sent by the form.
5. And last, the views
Sorry for the Bootstrap classes, but i wanted to style my example app real quick :-)
I guess that the HTML is pretty self-explanatory:
resources/views/settings/index.blade.php:
#extends('layout')
#section('content')
<h1>Settings example</h1>
#if(Session::has('updated'))
<div class="alert alert-success">
Your settings have been updated!
</div>
#endif
<form action="{!! route('settings.update') !!}" method="post">
{!! method_field('put') !!}
{!! csrf_field() !!}
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" id="title" name="title" placeholder="Title" value="{{ config('settings.title', 'Application Title') }}">
</div>
<div class="form-group">
<label for="Facebook">Facebook</label>
<input type="text" class="form-control" id="facebook" name="facebook" placeholder="Facebook URL" value="{{ config('settings.facebook', 'Facebook URL') }}">
</div>
<div class="form-group">
<label for="twitter">Twitter</label>
<input type="text" class="form-control" id="twitter" name="twitter" placeholder="Twitter URL" value="{{ config('settings.twitter', 'Twitter URL') }}">
</div>
<input type="submit" class="btn btn-primary" value="Update settings">
</form>
#stop
As you can see, when I try to get a value from the config, I also give it a default value, just in case it has not been set yet.
You can now create different forms for different setting groups. The form action should always be the settings.update route.
When I run the app, I can see the form with the default values:
When I type some values, hit the update button and Laravel redirects me to the form again, I can see a success message and my settings that now persist in the database.
You can inject the Request class. Lets update the title:
// Injecting Illuminate\Http\Request object
public function update(Request $request, $id, Factory $cache, Setting $setting)
{
$newTitle = $request->get('title');
$cache->forget('settings');
return redirect('admin');
}
To change the value in db, then could do:
$titleSetting = App\Setting::where('name', 'title')->first();
$titleSetting->value = $newTitle;
$titleSetting->save();
The whole code looks like:
public function update(Request $request, $id)
{
$newTitle = $request->get('title');
\Cache::forget('settings');
$titleSetting = App\Setting::where('name', 'title')->first();
$titleSetting->value = $newTitle;
$titleSetting->save();
return redirect('admin');
}
Related
My Route
Route::put('dataedit/{id}', [empDataController::class, 'empDataEdit'])->middleware('guest')->name('emp.data.edit');
My Controller
public function empDataEdit(empDataValidation $request, $id){
$id = Crypt::decrypt($id);
$DataAddCheck = EmpData::find($id)->update($request->all());
if($DataAddCheck){
return back()->with('successMsg', 'Data Added Successfully');
}
else{
return back()->with('successMsg', 'Something Went Wrong Try Again!');
}
}
Note : I add use App\Http\Requests\empDataValidation; in controller Top.
My Request Validation
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class empDataValidation extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'empName' => ['required'],
];
}
}
My blade (Just Important part)
<form id="msform" method="post" enctype="multipart/form-data"
action="{{ route('emp.data.edit', Crypt::encrypt($data->id)) }}">
#csrf
#method('PUT')
Note: When I use Request $request in controller It works Fine but when I use empDataValidation $request It not working, In my create controller empDataValidation $requestworks fine. Please help to solve this issue.
php artisan make:component Navbar, created:
App\View\Components\Navbar.php
app\resources\views\components\navbar.blade.php
Placing {{ auth()->user()->email }} or {{ Auth::user()->email }} in the blade file, gave this error:
Trying to get property 'email' of non-object.
Tried to fix this by changing my App\View\Components\Navbar.php to:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Navbar extends Component
{
public $email;
/**
* Create a new component instance.
*
* #return void
*/
public function __construct($email = null)
{
$this->email = 'info#example.com';
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
return view('components.navbar');
}
}
And added {{ $email }} to my blade file and it worked.
However, I want to display the e-mail address from the authenticated user, so I changed App\View\Components\Navbar.php to:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\Support\Facades\Auth;
class Navbar extends Component
{
public $email;
/**
* Create a new component instance.
*
* #return void
*/
public function __construct($email = null)
{
$this->email = Auth::user()->email;
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
return view('components.navbar');
}
}
And I got the same error again.
The error you're having because the user is not authenticated. Maybe add a check before calling the component in the blade file, wrap the component between #auth directive. Something like this
#auth
<x-navbar></x-navbar>
#endauth
when I try to edit a "Metier", a new "Metier" is created and the old one stays the same. I want to crush the old "Metier" and create a new one by mediting.
Controller
public function edit($id)
{
$metier=Metier::find($id);
return view('metier.edit',['libelle_metier'=>$metier]);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$metier=Metier::find($id);
return view('metier.edit',['libelle_metier'=>$metier]);
}
view
<div class="form-group">
<label for="">libelle Metier </label>
<input type="text" name ="libelle_metier" class="form-control"value ="{{$libelle_metier->libelle_metier}}" >
</div>
<div class="form-group">
<input type="submit" value = "enregistrer" class="form-control btn btn-primary">
</div>
As I see your update method is taking the user to edit form again, here is a good resource controller (MetierController) you can use.
This is just a sample to give an idea. To make it good there is a lot
more that can be done, like validations, exceptions and good
redirections.
MetierController.php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Metier;
use Illuminate\Http\Request;
class MetierController extends Controller
{
//inject the model to constructor
public function __construct(Metier $metier)
{
$this->metier = $metier;
}
//takes user to creation form
// GET
public function create()
{
return view('metier.create');
}
//when the create form is submitted
// POST
public function store(Request $request)
{
//some validations
//if the form field matches with db fields you can use $request->all()
$metier = $this->metier->create($request->all());
//assuming you have route names set
return redirect()->route('metier.show',$metier->id);
}
//takes user to edit form
// GET
public function edit($id)
{
$metier = $this->metier->find($id);
return view('metier.edit',compact('metier'));
}
//when the edit form is sublitted
// PATCH(POST)
public function update(Request $request, $id)
{
$metier = $this->metier->find($id);
//some validations
//if the form field matches with db fields you can use $request->all()
$metier->update($request->all());
//take user to somewhere when the update is done
return view('metier.edit',compact('metier'));
}
}
I set up a class for deleting comments left by users under an item model in a web page:
<?php
namespace App\Policies;
use App\Comment;
use App\Model_comment;
use App\User;
use App\Post;
class CommentPolicy
{
/**
*
* Defines if User can delete a Comment
*
* #param User $user
* #param Comment $comment
* #param Post $post
* #return bool
*/
public function deleteComment(User $user, Comment $comment, Post $post)
{
return ($user->id === $comment->user_id || $user->id === $post->user_id);
}
/**
* #param User $user
* #param Model_comment $model_comment
* #return bool
*/
public function deleteModelComment(User $user, Model_comment $model_comment)
{
return ($user->id === $model_comment->user_id);
}
}
Now I am trying to use this to show a button for deleting the comment.
While the "deleteComment" for posts comments is working the "deleteModelComment" is not passing at all, not any errors given.
The class is registered correctly under Auth Service Provider.
I am invoking it in the blade view like this:
#can('deleteModelComment', $c)
<button id="deletecmnt_{{$c->id}}" class="fa fa-minus pull-right"
aria-hidden="true"
data-token="{{ csrf_token() }}"></button>
#endcan
$c is the Model_comment. Any clue?
After trying out every solution I found on Google, I still cannot seem to access my relationship's data. I keep getting the following error:
Trying to get property of non-object (View: /home/eneko/foundry/biome/resources/views/home.blade.php)
Here are the files that load for this route:
Fruitu.php
<?php namespace Biome;
use Illuminate\Database\Eloquent\Model;
use Cviebrock\EloquentSluggable\SluggableInterface;
use Cviebrock\EloquentSluggable\SluggableTrait;
class Fruitu extends Model implements SluggableInterface {
use SluggableTrait;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'fruitus';
public function toString() {
return $this->izenburua;
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['izenburua', 'irudia', 'edukia'];
public function egilea() {
return $this->belongsTo('Biome\User');
}
protected $sluggable = array(
'build_from' => 'izenburua',
'save_to' => 'slug',
);
}
The controller function:
public function index()
{
$fruituak = Fruitu::all();
$fruituak->load('egilea');
return view('home')->with(compact('fruituak'));
}
The loop in the blade template:
#foreach ($fruituak as $fruitu)
<div class="fruitua">
<a href="{{ route('fruitu.show', $fruitu->slug) }}">
<h3>{{ $fruitu->izenburua }}</h3>
</a>
{{ $fruitu->egilea->name }}
<p class="eduk">{{ $fruitu->edukia }}</p>
</div>
#endforeach
Thanks in advance for your help!