I'm a newbie to voyager, is there a way I can upload users via CSV in voyager. I imagine I would have to make a custom template for this, anyone that can point me on the right direction on this, I would highly appreciate it.
I know this is an old question, but since this is the first thing I encounter when I research the same thing, I wanted to add an answer so that those who come here can benefit.
This solution is for Laravel 8.9.0 and Voyager 1.4
This has a selection before uploading CSV, it is not necessary but good to have, so you don't have to adjust CSV before uploading.
I did use another resource to do it, which was in Laravel 5.5 you can see the original codes from Laravel-5.5 CSV Import
I'll also add a fork which updated with Laravel 9.0 after I finished my own project.
Anyhow, I use Laravel-Excel 3.1.24 to read CSV. The resource was using an older version.
Let's Start the Coding
Before you connect it with Voyager, first you should know that you need to write your view and controller to do the job.
Also, we'll need our model. For your User Model, make sure that you added all the fields you need. Create migration with the same needs.
Like if your User Model has this;
protected $fillable = [
'name',
'email',
'password',
];
Make sure your migration;
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
After you set your model and migration, you'll need one other model to store your CSV. This will be helpful for you while doing the selection with CSV and database fields.
It will have only three fields; 'csv_filename', 'csv_header', 'csv_data' .
After setting our models, we'll need to add config/app.php to our selections.
'db_fields' => [
'name',
'email',
'password',
]
That fields we will get from CSV, you can remove the password if you don't have password in your CSV file.
Before getting into our Controller, we should add Laravel-Excel. From the documentation, all you need to do is run this command on the terminal.
composer require "maatwebsite/excel:3.1.24"
This will add Laravel-Excel to our project. Our controller will need "import" which is a class to helps us import files, comes with Laravel-Excel. For this;
php artisan make:import CsvImport --model=User
You can use it for a quick start like Laravel-Excel documentation recommended. Now we can get into our Controller. We'll create controller vie Laravel commands. We're gonna need bunch of things;
use App\Imports\CsvImport; //This is needed for Laravel-Excel.
use Illuminate\Http\Request;
use App\Models\User; //Our user model.
use App\Models\CsvData; //Our model for csv.
use App\Http\Requests\CsvImportRequest; //I'll share this in a sec.
use Maatwebsite\Excel\Facades\Excel; //This is from Laravel-Excel
use Illuminate\Support\Str; //This is for basic password creation
class ImportController extends Controller
{
The functions we need are;
public function form()
{
return view('import.form');
}
This function is pretty simple. It will return our view. The second one is when we parse our CSV and saving in it add CSV_Data. But first, we need a request as Laravel wanted.
php artisan make:request CSVImportRequest
I'll share mine;
class CsvImportRequest extends FormRequest
{
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'csv_file' => 'required|file'
];
}
Now we can go back to our Controller;
public function parseImport(CsvImportRequest $request)
{
//we getting with the request the file. So you need to create request with
//Laravel. And you should add this to your Controller as use App\Http\Requests\CsvImportRequest;
$path = $request->file('csv_file')->getRealPath();
if ($request->has('header')) {
//this is coming from Laravel-Excel package. Make sure you added to your
//controller; use Maatwebsite\Excel\Facades\Excel;
$data = Excel::toArray(new CsvImport, request()->file('csv_file'))[0];
} else {
$data = array_map('str_getcsv', file($path));
}
if (count($data) > 0) {
//checking if the header option selected
if ($request->has('header')) {
$csv_header_fields = [];
foreach ($data[0] as $key => $value) {
$csv_header_fields[] = $key;
}
}
$csv_data = array_slice($data, 0, 2);
//creating csvdata for our database
$csv_data_file = CsvData::create([
'csv_filename' => $request->file('csv_file')->getClientOriginalName(),
'csv_header' => $request->has('header'),
'csv_data' => json_encode($data)
]);
} else {
return redirect()->back();
}
//this is the view when we go after we submit our form.We're sending our data so we can select to match with db_fields.
return view('import.fields', compact('csv_header_fields', 'csv_data', 'csv_data_file'));
}
And, now the import function.
public function processImport(Request $request)
{//we are getting data from request to match the fields.
$data = CsvData::find($request->csv_data_file_id);
$csv_data = json_decode($data->csv_data, true);
$request->fields = array_flip($request->fields);
foreach ($csv_data as $row) {
$contact = new User();
foreach (config('app.db_fields') as $index => $field) {
//using config app.db_fields while matching with request fields
if ($data->csv_header) {
if ($field == "null") {
continue;
} else if ($field == "password") {
//this is checkin if password option is set. If not, it is creating a password. You can eliminate this according to your needs.
if (isset($request->fields['password'])) {
$pw = $row[$request->fields['password']];
} else {
$pw = Str::random(10);
}
} else
$contact->$field = $row[$request->fields[$field]];
} else
//same with the if but without headers. You can create a function to avoid writing
//codes twice.
{
if ($field == "null") {
continue;
} else if ($field == "password") {
if (isset($request->fields['password'])) {
$pw = $row[$request->fields['password']];
} else {
$pw = Str::random(10);
}
} else
$contact->$field = $row[$request->fields[$index]];
}
}
$user = User::where(['email' => $contact->email])->first();
//checking for duplicate
if (empty($user)) {
$contact->password = bcrypt($pw);
$contact->save();
} else {
$duplicated[] = $contact->email;
//if you want you can keep the duplicated ones to check which ones are duplicated
}
}
//you can redirect wherever you want. I didn't need an success view so I returned
//voyagers original users view to see my data.
return redirect(route('voyager.users.index'));
}
Now, we can create our routes. Since we're using Voyager do not forget to add voyager., so we can use it with the voyager admin panel.
use App\Http\Controllers\ImportController;
Route::group(['prefix' => 'admin','as' => 'voyager.', 'middleware' => 'admin.user'],
function()
{
Route::get('import',[ImportController::class, 'form'])->name("import.form");
Route::post('import/parse', [ImportController::class, 'parseImport'])-
>name("import.parse");
Route::post('import/process', [ImportController::class, 'processImport'])-
>name("import.process");
});
Also, we'll need a view while importing and selecting. I created my views in the views/import.
For your form blade, you want to use it with voyager so you should
#extends('voyager::master')
use this to have the same theme. After that, you need to add your html with a section.
#section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">CSV Import</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('voyager.import.parse') }}" enctype="multipart/form-data">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('csv_file') ? ' has-error' : '' }}">
<label for="csv_file" class="col-md-4 control-label">CSV file to import</label>
<div class="col-md-6">
<input id="csv_file" type="file" class="form-control" name="csv_file" required>
#if ($errors->has('csv_file'))
<span class="help-block">
<strong>{{ $errors->first('csv_file') }}</strong>
</span>
#endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<label>
<input type="checkbox" name="header" checked> File contains header row?
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-8 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Parse CSV
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
#endsection
As you can see, we use "voyager." to add our route. This will also help us to Voyager settings. Our second view is where we choose our db_fields.
#extends('voyager::master')
#section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">CSV Import</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('voyager.import.process') }}">
{{ csrf_field() }}
<input type="hidden" name="csv_data_file_id" value="{{ $csv_data_file->id }}" />
<table class="table">
#if (isset($csv_header_fields))
<tr>
#foreach ($csv_header_fields as $csv_header_field)
<th>{{ $csv_header_field }}</th>
#endforeach
</tr>
#endif
#foreach ($csv_data as $row)
<tr>
#foreach ($row as $key => $value)
<td>{{ $value }}</td>
#endforeach
</tr>
#endforeach
<tr>
#foreach ($csv_data[0] as $key => $value)
<td>
<select name="fields[{{ $key }}]">
<option value="null">Do Not Save</option>
#foreach (config('app.db_fields') as $db_field)
<option value="{{ (\Request::has('header')) ? $db_field : $loop->index }}"
#if ($key === $db_field) selected #endif>{{ $db_field }}</option>
#endforeach
</select>
</td>
#endforeach
</tr>
</table>
<button type="submit" class="btn btn-primary">
Import Data
</button>
</form>
</div>
</div>
</div>
</div>
</div>
#endsection
This is it! Now we have our import set. We just need to configure with Voyager.
VOYAGER SETTINGS
First, we need a menu-item to reach our import view. After you get into Voyager panel, you need to go to the menu builder to create a menu item. All you have to do is make the url of the item as /admin/import with that when you clicked that item, you'll go to our import form view. For the other options, you can change them however you want.
You may also need to change from your user BREAD Model as App\Models\User, so it can receive all the fields we created.
laravel-8.9.0 voyager-1.4 laravel-excel-3.1.24
Related
I'm beginner in laravel and I want to update multiple checkboxes in database ..
when I click at update button automatically my inputs show old value also my permissions are checked by old value to update it ..
relation between user and permission is manytomany .. I have another table named userpermissions who has id_user and id_permission
this is my update form in ( edit.blade.php)
<form action="{{ url('users/'.$user->id) }}" method="POST">
#csrf
#method('PUT')
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Name</label>
<input type="text" name="name" id="name" required class="form-control" value="{{ $user->name }}">
#error('name')
<ul class="alert"><li class="text-danger">{{ $message }}</li></ul>
#enderror
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Email</label>
<input type="email" name="email" id="email" required class="form-control" value="{{ $user->email }}">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
#foreach($permissions as $permission)
<input type="checkbox" name="data[]" value="{{ $permission->id }}"
<?php if( in_array($permission->id, $user->userPermissions->pluck('permission_id')->toArray())){ echo 'checked="checked"'; } ?>/>
{{ $permission->name }}
#if($loop->iteration % 3 == 0 ) <br> #else #endif
#endforeach
</div>
</div>
</div>
<div class="text-right mt-4">
<button type="submit" class="btn btn-primary"> Add</button>
</div>
</form>
and this is my controller where I think have a problem with methods :
edit function
public function edit(User $user)
{
$permissions = Permission::get();
return view('users.edit', compact('user','permissions'));
}
update function :
public function update(UserRequest $request,User $user)
{
$user->update(
$request->only('name', 'email')
);
$user->userPermissions()->save($request->input('data'));
return redirect()->back()->with('status','user updated !');
}
and this is my functio store :
public function store(UserRequest $request)
{
$this->validate($request, [
'name' => 'required',
'email'=>'required|email',
'password' => 'required|confirmed|min:6',
]);
$user = User::create(
$request->only('name', 'email', 'password')
);
$user->userPermissions()->createMany($request->input('data'));
return redirect()->back()->with('status','Utilisateur ajouté !');
}
Thanks for advance !
$user->userPermissions()->save($request->input('data'));
One important thing to understand here, is that save() on relation doesn't remove old values from pivot table, it just add more values to it(no distinction check). You need something like refresh functionality. Look at attaching\detaching or sync, second one is more convenient.
In first case before saving permissions you can do this
// remove all old permissions
$user->userPermissions()->detach();
// update them with new one
$user->userPermissions()->attach($request->input('data'));
In second case, which is less verbose then first one you just need to pass and array of permissions to user object.
// this will do both things which we did before
$user->userPermissions()->sync($request->input('data'))
But i encourage you to read the docs and ask questions after ;)
Another thing which i saw and its not related to the current topic is
$user->userPermissions->pluck('permission_id')->toArray()
you are using lazy load inside of foreach loop which means that on each iteration of the loop you are making a query to the database(N + 1 problem). You can preload/eager load userPermissions instead of loading them on a fly by declaring with relation in your User model like this
class User extends Model
{
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['userPermissions'];
...
}
and then in your User object will have userPermissions property which you can compare to permissions.
Hope that you get main idea and info was useful for you!
At the moment I am working with Laravel. I am trying to insert data into a database. It is not user data, but product data. Costumers have to be able to insert a title, description and price of a product into the database.
I have looked at the laravel website, however, I was unable to find anything. There are some people with the same question as mine on StackOverflow. However, the answers that were given to them do not work for me.
My controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProductsController extends Controller
{
public function insertform(){
return view('home');
}
public function insert(Request $request){
$productname = $request->input('title');
$description = $request->input('description');
$price = $request->input('price');
$data=array('title'=>$productname,"description"=>$description,"price"=>$price);
DB::table('products')->insert($data);
echo "Record inserted successfully.<br/>";
echo 'Click Here to go back.';
}
}
My view:
#section('content')
<h1>Add your new items here:</h1>
<form method="get">
<div class="title">
<div class="title">
<span class="input-group-text" id="title">Title</span>
</div>
<input type="text" name="title" class="form-control" aria-label="title" aria-describedby="inputGroup-sizing-default">
</div>
<br>
<br>
<div class="description">
<div class="description">
<span class="input-group-text" id="description">Description</span>
</div>
<input type="text" name="description" class="form-control" aria-label="description" aria-describedby="inputGroup-sizing-default">
</div>
<br>
<br>
<div class="price">
<div class="price">
<span class="input-group-text" id="price">Price</span>
</div>
<input type="text" name="price" class="form-control" aria-label="price" aria-describedby="inputGroup-sizing-default">
</div>
<br>
<br>
<div class="form-group">
<label for="exampleFormControlFile1">Insert Image</label>
<input type="file" class="form-control-file" id="exampleFormControlFile1">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
#endsection
My web.php:
<?php
Route::get('/', function () {
return view('welcome');
});
Route::get('insert','ProductsController#insertform');
Route::post('create','ProductsController#insert');
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
My database structure:
The home and welcome, along with some code in the web.php, has been made by authentication.
Hopefuly you guys can help me out. I want to make sure that the product data is inserted into the database.
Don't use DB class. Instead create a model called Product and use model function to create or update data into table.
php artisan make:model Product
$product= Product::create([
'name' => $request->name, # declared as fillable on Product model
'description' => $request->description,
...
]);
Convert the route of /insert into POST and add csrf field in your form
#csrf
OR
<input type="hidden" name="_token" value="{{csrf_token()}}">
On your controller validation of input in insert function.
Also take a look at these -
https://laravel.com/docs/5.8/eloquent#defining-models
Laravel Validation Rules
or https://laravel.com/docs/5.8/validation#quick-writing-the-validation-logic
In your web.php, Add route names
Route::get('insert','ProductsController#insertform')->name('product.create');
Route::post('create','ProductsController#insert')->name('product.store');
In your view, change method to post and add action attribute and csrf field.
<form action="{{ route('product.store') }}" method="post">
#csrf
In Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use DB;
class ProductsController extends Controller
{
public function insertform(){
return view('home');
}
public function insert(Request $request){
$productname = $request->input('title');
$description = $request->input('description');
$price = $request->input('price');
$data = array(
"title" => $productname,
"description" => $description,
"price" => $price
);
DB::table('products')->insert($data);
echo "Record inserted successfully.<br/>";
echo 'Click Here to go back.';
}
}
Alternate you can directly add action without route name
<form action="/create" method="post">
#csrf
In laravel 5.6 i can show you how to insert the data and display the data to the index page
so first of all i can code my route
in here we can use 2 routes
first is index page route
second is store and in store controller you can display your stored data.
Route::get('/FAQ_page', 'SettingController#FAQ_page')->name('FAQ_page');
Route::get('/FAQ_page/create', 'SettingController#FAQ_page_create')->name('FAQ_page.create');
Route::post('/FAQ_page/store', 'SettingController#FAQ_pagestore');
now make a database and connect to your module
this is your module
namespace App;
use Illuminate\Database\Eloquent\Model;
class FAQpage extends Model
{
protected $table = 'p66_FAQ_page';
public $timestamps = false;
protected $primaryKey = 'fid';
}
now make your controller like this
public function FAQ_page()
{
$data = FAQpage::get();
return view('SuperAdmin.settings.FAQ_page', compact('data'));
}
public function FAQ_page_create()
{
return view('SuperAdmin.settings.FAQ_page_create');
}
public function FAQ_pagestore(Request $request)
{
request()->validate([
'FAQ_question'=> 'required',
'FAQ_answer'=> 'required',
'Sort_order'=> 'required|max:4',
'FAQ_departments'=> 'required',
]);
$data = new FAQpage();
$data->FAQ_question = $request->get('FAQ_question');
$data->FAQ_answer = $request->get('FAQ_answer');
$data->Sort_order = $request->get('Sort_order');
$data->FAQ_departments = $request->get('FAQ_departments');
$data->Created_date = Carbon::now();
$data->save();
return redirect('/SuperAdmin/FAQ_page');
}
thank you
I'm learning Laravel and I got stuck trying to get data from a form.
I already am able to get data back with GET, but with POST I've been having a ton of trouble. Here's what I'm working with:
Form:
<form id="forms" method="POST" action="sugestoes" novalidate>
{{ csrf_field() }}
<div class="form-row">
<div class="form-group col-md-12">
<label for="obs">Observações:</label>
<textarea type="text" class="form-control" name="obs" placeholder="Observações" required></textarea>
</div>
</div>
<hr>
<button type="submit" class="btn btn-primary">Enviar</button>
</form>
#php
if (isset($_POST["obs"])) {
echo "IN";
}
#endphp
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function store(Request $request)
{
$name = $request->input('obs');
return redirect('sugestoes');
//
}
}
Route:
Route::post('sugestoes', 'PostController#store');
The intended behaviour that I'm trying to reach is for the post to be submitted, and then returning to the same page with an empty form. Later on I'll be sending the input data into a database, but for now I just want to get the post to work.
I guess I'm missing something really basic, but I've been following guides and looking online, I've done some progress but I'm really stuck here.
(some more info, this is Laravel 5.4, and I'm using XAMPP)
First, you need to call the model, use App/Your_model_name; then you have to save the data.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Suggest; //Suggest model, let's hope you have suggest table
class PostController extends Controller
{
public function store(Request $request)
{
$suggest = new Suggest; //model
$suggest->name = $request->obs; //name is DB name, obs is request name
$suggest->save(); //save the post to DB
return redirect()->back()->with('success', 'Saved successfully'); //return back with message
}
}
Then if you want to flash the message on the HTML page
#if(session('success'))
<div class="alert alert-warning alert-dismissible" id="error-alert">
<strong style="color: white;">{{session('success')}}</strong>
</div>
#endif
<form id="forms" method="POST" action="{{ route('sugestoes') }}" novalidate>
{{ csrf_field() }}
<div class="form-row">
<div class="form-group col-md-12">
<label for="obs">Observações:</label>
<textarea type="text" class="form-control" name="obs" placeholder="Observações" required></textarea>
</div>
</div>
<button type="submit" class="btn btn-primary">Enviar</button>
</form>
Remove the #php tag below the form, then in router.php
Route::post('/sugestoes', 'PostController#store')->name('sugestoes');
Then in Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function store(Request $request)
{
$name = $request->input('obs');
return redirect('/sugestoes'); // you should have GET in Route.php
//
}
}
Add the following code in your action attribute on the form. It will capture the post URL. When you submit the form it will send the form data to the URL end-point.
action="{{ url('sugestoes')}}"
Then die and dump in your controller store function
public function store(Request $request)
{
dd($request->all());
}
It's my first time using blade and I'm a little confused with this. I need to show a form like what it comes with the default edit-add view in Voyager, but I need to display some inputs based on roles. I am filtering by the current user that is logged in and that is working fine, but I need to hide/display some inputs of the form based on that.
How can I accomplish this in the blade view?
Here is how Voyager take the data in the blade view:
#php
$dataTypeRows = $dataType->{(isset($dataTypeContent->id) ? 'editRows' :'addRows' )};
#endphp
#foreach($dataTypeRows as $row)
<!-- GET THE DISPLAY OPTIONS -->
#php
$options = json_decode($row->details);
$display_options = isset($options->display) ? $options->display : NULL;
#endphp
#if ($options && isset($options->formfields_custom))
#include('voyager::formfields.custom.' . $options->formfields_custom)
#else
<div class="form-group #if($row->type == 'hidden') hidden #endif #if(isset($display_options->width)){{ 'col-md-' . $display_options->width }}#else{{ '' }}#endif" #if(isset($display_options->id)){{ "id=$display_options->id" }}#endif>
{{ $row->slugify }}
<label for="name">{{ $row->display_name }}</label>
#include('voyager::multilingual.input-hidden-bread-edit-add')
#if($row->type == 'relationship')
#include('voyager::formfields.relationship')
#else
{!! app('voyager')->formField($row, $dataType, $dataTypeContent) !!}
#endif
#foreach (app('voyager')->afterFormFields($row, $dataType, $dataTypeContent) as $after)
{!! $after->handle($row, $dataType, $dataTypeContent) !!}
#endforeach
</div>
#endif
#endforeach
If my table 'example' contains: id, name, address and phone. How do I display in this view form only inputs for name and address?
Thanks in advance.
You should try this:
public function edit(Request $request, $id)
{
$user = Example::find($id);
return view(‘viewname’,compact(‘user’));
}
edit-add.blade.php
<input type = “text” name=”name” value=”#if(isset($user->name)){{ old('name', $user->name) }}#else{{old('name')}}#endif”>
<input type = “text” name=”name” value=”#if(isset($user->address)){{ old('address', $user->address) }}#else{{old('address')}}#endif”>
i cant understand what do you mean but
for showing a value in input in a view first you should send the value to view for example in controller you have :
public function test()
{
.....
return view('myView',compact('user'));
}
then in your view :
<input type="text" value"{{$user->address}}">
<input type="text" value"{{$user->first_name}}">
You can edit it from ADMIN interface without using any code
Just go to the settings menu and click BREAD
I am making a todo list with validation using laravel 5.4.
When I click on the submit button, only the required validation is working but not the unique.
What am I doing wrong and how do I fix it so as to get it working as desired?
Below is my form (located at home.blade.php):
<div class="panel-body">
<form class="form form-control" action="/todo" method="post">
{{csrf_field()}}
<fieldset class="form-group">
<textarea class="form-control" name="textbox" id="textArea"></textarea>
<button type="submit" class="btn btn-primary">Submit</button>
</fieldset>
</form>
{{-- for dispaying the error --}}
#if (count($errors) >0)
{{-- expr --}}
#foreach ($errors->all() as $error)
<h3 class="text-danger">{{$error}}</h3>
#endforeach
#endif
</div>
Here, the content of my Todo controller (in my todocontroller.php file):
use Illuminate\Http\Request;
use App\todo;
public function store(Request $request)
{
$todo = new todo;
$todo->body = $request->textbox;
$this->validate($request,[
"body" => "required|unique:todos"
]);
$todo->save();
return redirect('/todo');
}
You should simply use the name of the field; you don't need to stress yourself.
Take a look at the snippet below:
<?php
namespace App\Http\Controllers;
use App\Todo;// following Laravel's standards, your model name should be Todo; not todo
use Illuminate\Http\Request;
class NameOfYourTodoController extends Controller
{
public function store(Request $request)
{
$todo = new Todo();
// use the name of the field directly (here, textbox)
$this->validate($request, [
'textbox' => 'required|unique:todos'
]);
// other code logics here.
}
}