So i'am trying to make some widget service, but got one problem. In my dashboard i including number of partial views 'widget' and passing to it parameter 'settings'. Everything should be ok to me, but i want to read this 'settings' parameter in my view composer so i should be able to pass correct 'newData'
to 'widget' view. I found something like $view->getData(); but when i use this infinite loop accurs. Any suggestions?
Note: i MUST use view composer, and CAN'T pass this 'newData' while including.
index.blade.php
<p>
#include('widget', ['settings'=>'bla'])
</p>
Some service provider:
public function boot()
{
view()->composer(
'widget', 'App\Http\ViewComposers\WidgetComposer'
);
}
App\Http\ViewComposers\WidgetComposer.php
class WidgetComposer
{
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$view->with('newData', 'someArrayWithData');
}
}
widget.blade.php
<p>
{!! $newData !!}
</p>
Ok, so it should be $view->getData();
class WidgetComposer
{
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$settings = $view->getData()['settings'];
$data = $settings == 'bla' ? 'got bla' : array();
$view->with('newData', $data);
}
}
This works clean project, however in my main project this causes infinity loop in renderer. Not sure if this is because of some variables that i shouldnt be using here or because of some dependicies. I will add comment if found out something.
Related
I'm creating a survey and in order to create a question I need to get the survey_id from the survey table and place in it a create a question view in a hidden field in a form and pass it to the question controller.
Since you passe survey_id all you need to the do is use the right model (Question) in the store method, everything else same correct.
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$input = $request->all();
Question::create($input); //<==== You need to use Question insteed of Survey
return redirect('survey');
}
You can try below code,i hope this is help you:
$input = $request->all();
$survey = new Survey ();
$survey ->fill($input)->save();
if (isset($input['survey_id']) && !empty($input['survey_id'])) {
$survey_id= $input['survey_id'];
foreach ($survey_id as $index => $value) {
Question::create([
'survey_id' => $survey ->id,
'title' => $value
]);
}
}
#Rachid's comments is right, but its not the reason of the error...
The problem is the resource's route -> controler#action mapping doesn't include any param for the /create route as we can see bellow...
... and your QuestionController::create function needs the param $id.
//...
class QuestionController extends Controller
{
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create($id) // <---- Right here...
//...
So, to bind your foreign key to this resource i'll need to do something like this...
Route::resource('survey.question', 'QuestionController');
This way you'll have your routes mapped sothing like this...
GET /survey/{survey}/question survey.question.index
GET /survey/{survey}/question/create survey.question.create
Then change your template, from:
{!! Form::open(array('action' => 'QuestionController#store', 'id' => 'Question')) !!}
To:
{!! Form::open(array('action' => 'QuestionController#store', 'id' => 'Question'), ['survey' => Request::route('survey')]) !!}
Then you can use your QuestionController::create function like this...
public function create(Survey $survey)
{
return view('question.create')->with('survey', $survey);
}
You can also use parameters in resourceful routes like this:
Route::resource('survey/{survey}/question', 'QuestionController');
Then in your controller you can use:
class QuestionController extends Controller
{
// ...
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create($id)
{
// $id will be the {survey} id from the route parameter.
}
// ...
}
You could as well use route model binding to avoid the manual Survey retrival in the controller:
use App\Survey;
class QuestionController extends Controller
{
// ...
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create(Survey $survey)
{
return view('question.create', compact('survey'));
}
// ...
}
Remember to update the form action as well to include the pameter
Be Careful!
If you register the parameter in the resource like I have shown, methods as show, edit, update and destroy (that accepts the id of the resource to act on in the url) will receive two route parameters, one will be the survey id, the second one would be the question id (your resource).
Keep that in mind!
I'm trying to store all my settings from my settings table into a global variable, but I'm stucked now(I have no idea what's the next step), this is my actual model and seeder:
model - Settings.php
class Setting extends Model
{
protected $table = 'settings';
public $timestamps = false;
protected $fillable = [
'name',
'value',
];
}
seeder - SettingsTableSeeder.php
class SettingsTableSeeder extends Seeder
{
public function run()
{
$settings = [
['name' => 'title', 'value' => ''],
['name' => 'facebook', 'value' => ''],
['name' => 'twitter', 'value' => ''],
['name' => 'instagram', 'value' => '']
];
foreach($settings as $setting){
\App\Setting::create($setting);
}
}
}
How can I store all the data inside the settings table and make then acessible from blade, or any controller or view?
Edit
Now, my question is, how can i update a single or multiple value(s) from a form?
I have set this up:
My route:
Route::put('/', ['as' => 'setting.update', 'uses' => 'Admin\AdminConfiguracoesController#update']);
My Admin\AdminConfiguracoesController:
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)
{
$this->repository->findByName($setting);
$cache->forget('settings');
return redirect('admin');
}
}
My SettingRepository:
class SettingRepository
{
private $model;
public function __construct(Setting $model)
{
$this->model = $model;
}
public function findByName($name){
return $this->model->where('name', $name)->update();
}
}
My 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">TÃtulo do artigo</div>
{!! Form::text('title', null, ['placeholder' => 'Nome do site']) !!}
#if($errors->has('title'))
<div class="item-desc">{{ $errors->first('title') }}</div>
#endif
</div>
<div class="s-form-item s-btn-group s-btns-right">
Voltar
<input class="s-btn" type="submit" value="Atualizar">
</div>
{!! Form::close() !!}
But things does not work. How can I update the values into the table?
See improved answer in Update 2
I would add a dedicated Service Provider for this. It will read all your settings stored in the database and add them to Laravels config. This way there is only one database request for the settings and you can access the configuration in all controllers and views like this:
config('settings.facebook');
Step 1: Create the Service Provider.
You can create the Service Provider with artisan:
php artisan make:provider SettingsServiceProvider
This will create the file app/Providers/SettingsServiceProvider.php.
Step 2: Add this to the boot-method of the provider you have just created:
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
// Laravel >= 5.2, use 'lists' instead of 'pluck' for Laravel <= 5.1
config()->set('settings', \App\Setting::pluck('value', 'name')->all());
}
From the Laravel Docs:
[The boot method] is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework.
http://laravel.com/docs/5.1/providers#the-boot-method
Step 3: Register the provider in your App.
Add this line to the providers array in config/app.php:
App\Providers\SettingsServiceProvider::class,
And that's it. Happy coding!
Update: I want to add that the boot-method supports dependency injection. So instead of hard coding \App\Setting, you could inject a repository / an interface that is bound to the repository, which is great for testing.
Update 2: As Jeemusu mentioned in his comment, the app will query the database on every request. In order to hinder that, you can cache the settings. There are basically two ways you can do that.
Put the data into the cache every time the admin is updating the
settings.
Just remember the settings in the cache for some time and clear the cache every time the admin updates the settings.
To make thinks more fault tolerant, I'd use the second option. Caches can be cleared unintentionally. The first option will fail on fresh installations as long as the admin did not set the settings or you reinstall after a server crash.
For the second option, change the Service Providers boot-method:
/**
* Bootstrap the application services.
*
* #param \Illuminate\Contracts\Cache\Factory $cache
* #param \App\Setting $settings
*
* #return void
*/
public function boot(Factory $cache, Setting $settings)
{
$settings = $cache->remember('settings', 60, function() use ($settings)
{
// Laravel >= 5.2, use 'lists' instead of 'pluck' for Laravel <= 5.1
return $settings->pluck('value', 'name')->all();
});
config()->set('settings', $settings);
}
Now you only have to make the cache forget the settings key after the admin updates the settings:
/**
* Updates the settings.
*
* #param int $id
* #param \Illuminate\Contracts\Cache\Factory $cache
*
* #return \Illuminate\Http\RedirectResponse
*/
public function update($id, Factory $cache)
{
// ...
// When the settings have been updated, clear the cache for the key 'settings':
$cache->forget('settings');
// E.g., redirect back to the settings index page with a success flash message
return redirect()->route('admin.settings.index')
->with('updated', true);
}
To avoid querying the database on each request, you should save the settings to a config file each time they are changed by the admin/user.
// Grab settings from database as a list
$settings = \App\Setting::lists('value', 'name')->all();
// Generate and save config file
$filePath = config_path() . '/settings.php';
$content = '<?php return ' . var_export($settings, true) . ';';
File::put($filePath, $content);
The above will create a Laraval compatible config file that essentially just returns an array of key => values. The generated file will look something like this.
<?php
return array(
name => 'value',
name => 'value',
);
Any php file in the /config directory will be auto-included by Laravel and the array variables accessible to your application via the config() helper:
config('settings.variable_name');
I want to share my use case, my answer may not be directly answering OP, but hope answering future developers.
This is tested in Laravel 8 application, but i believe it will work fine from Laravel version 5.5 and up.
In my case, I have a settings table with key and value fields as you may see in this migration file.
<?php
use...;
class CreateSettingsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('settings', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->text('value');
$table->timestamps();
});
}
//...
}
I heavily use the values stored in this table in my application, so to gain on performance i store those key/value in a config file, I do that anytime the admin has updated a value in the back office.
For that purpose i use Eloquent model events, to be more precise I use saved event, because the saving/saved events will dispatch when a model is created or updated.
<?php
namespace App\Models;
use...;
class Setting extends Model
{
use HasFactory;
//...
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::saved(function () {
$settings = static::pluck('value', 'key')->toArray();
$stringify_settings = var_export($settings, true);
$content = "<?php return {$stringify_settings};";
File::put(config_path('app_settings.php'), $content);
});
}
}
I did one more thing, I added /config/app_settings.php to .gitignore file.
Resources: Store settings table in a configuration file
You can store the data in the database just like you do it normally in Laravel. \App\Setting::create(), \App\Setting::new() and other methods.
For using the values in blade, you can do {{\App\Setting::where('name','title')->pluck('value')}}
And, you can also use scopes for this.
class Setting extends Model
{
public function scopeFor($query, $settingName)
{
return $query->where('name', $settingName);
}
}
then you could use \App\Setting::for('title')->pluck('value')
I'm having problems with my routes in laravel when I go to "/todos and /todos/id " everything works perfectly but when I try using the "/todos/create" I get a No query results for model [TodoList]
I'm new to this please help me... i really dont want to give up because i really love this mvc
here's my routes
Route::get('/', 'TodoListController#index');
Route::get('todos', 'TodoListController#index');
Route::get('/todos/{id}', 'TodoListController#show');
Route::get('db', function() {
$result = DB::table('todo_lists')->where('name', 'Your List')->first();
return $result->name;
});
Route::resource('todos', 'TodoListController');
model
<?php
class TodoList extends Eloquent {}
Controller
public function index()
{
$todo_lists = TodoList::all();
return View::make('todos.index')->with('todo_lists', $todo_lists);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$list = new TodoList();
$list->name = "another list";
$list->save();
return "I am here by accident";
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$list = TodoList::findOrFail($id);
return View::make('todos.show')->withList($list);
}
my views
#extends('layouts.main') #section('content')
<h1>All todo list</h1>
<ul>
#foreach ($todo_lists as $list)
<li>{{{ $list->name }}}</li>
#endforeach
</ul>
#stop
The issue is with the explicit route you defined for /todos/{id}. Since this route is defined before the resource route, it is catching the route for todos/create, and treating the text create as the {id} parameter for the show method.
Delete the explicit get routes for todos and /todos/create and your issue will be fixed. Both of these routes are handled by the resource route.
Your model doesn't have the fillable attribute. Try adding
protected $fillable = [
'name'
];
You also need to define a table
protected $table = 'todolist';
Extra
You're also manually adding some routes that are already defined by your resource controller like your show route.
You are also storing models in your create method. You should be showing a form in your create method and storing results in your store method.
I'm folowing a tutorial on https://laracasts.com/series/laravel-5-fundamentals
then I started digging into nested controllers from this tutorial https://www.flynsarmy.com/2015/02/creating-a-basic-todo-application-in-laravel-5-part-4/
I've got a similar logic Project which has one hypothesis.
So I've setup my nested routes
Route::resource('project','ProjectsController');
Route::resource('project.hypothesis','HypothesisController');
Then created a form for adding a hypothesis to a Project
{!! Form::model(new App\Hypothesis, ['route' => ['project.hypothesis.store', $project->id]]) !!}
#include ('hypothesis.form',['submitButtonText'=>'create']);
{!! Form::close() !!}
I also created a HyphothesisRequest class with basic validation rules
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class HyphothesisRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'description' =>'required'
];
}
}
Now according to the above tutorials in my controller i've got
public function store(Project $project, HyphothesisRequest $request)
{
$this->validate($request);
$h = new Hypothesis;
$h->description = $request->description;
$h->project_id = $project->id;
$h->save();
return Redirect::route('project.show', $project->id);
}
The problem is that when the HyphothesisRequest $request is passed as an argument I get an forbidden page from laravel. When I remove this it goes to the desired page but without validation.
I'm at the basic level of this so please be patient :)
Try change
public function authorize()
{
return false;
}
to
public function authorize()
{
return true;
}
In ZF1 I used to declare variables in the application.ini
brandname = "Example"
weburl = "http://www.example.com/"
assetsurl = "http://assets.example.com/"
And in the Bootstrap I did this so i could access them in the view
define('BRANDNAME', $this->getApplication()->getOption("brandname"));
define('WEBURL', $this->getApplication()->getOption("weburl"));
define('ASSETSURL', $this->getApplication()->getOption("assetsurl"));
Whats the ZF2 way to do this, I know that i can create an array in the local.php config file like:
return array(
'example' => array(
'brandname' => 'Example',
'weburl' => 'http://www.example.com/',
'asseturl' => 'http://assets.example.com/',
),
);
When I want to access that variable in the controller I can do
$config = $this->getServiceLocator()->get('Config');
$config['example']['brandname']);
So far so good... but how do i access this variable in the view?
I don't want to create a view variable for it in every controller. And when i try the above in a view phtml file i get an error.
Zend\View\HelperPluginManager::get was unable to fetch or create an instance for getServiceLocator
Any ideas?
You could create a sinmple view helper to act as a proxy for your config, (totally un tested).
Module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'configItem' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$viewHelper = new View\Helper\ConfigItem();
$viewHelper->setServiceLocator($serviceLocator);
return $viewHelper;
}
),
);
}
ConfigItem.php
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\ServiceManager\ServiceManager;
/**
* Returns total value (with tax)
*
*/
class ConfigItem extends AbstractHelper
{
/**
* Service Locator
* #var ServiceManager
*/
protected $serviceLocator;
/**
* __invoke
*
* #access public
* #param string
* #return String
*/
public function __invoke($value)
{
$config = $this->serviceLocator->get('config');
if(isset($config[$value])) {
return $config[$value];
}
return NULL;
// we could return a default value, or throw exception etc here
}
/**
* Setter for $serviceLocator
* #param ServiceManager $serviceLocator
*/
public function setServiceLocator(ServiceManager $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
}
You could then do something like this in your view, assuming you have them set in your config of course :)
echo $this->configItem('config_key');
echo $this->configItem('web_url');
I would personally tend to just pass the values through to the view every time though, keeping the view a dumb as possible.
I answered this before on a different post.
/* Inside your action controller method */
// Passing Var Data to Your Layout
$this->layout()->setVariable('stack', 'overflow');
// Passing Var Data to Your Template
$viewModel = new ViewModel(array( 'stack' => 'overflow' ));
/* In Either layout.phtml or {Your Template File}.phtml */
echo $this->stack; // Will print overview
That's it... No need to mess with view helpers, event manager, service manager, or anything else.
Enjoy!