I try to import with skip the line containing already existing email but it doesn't work - laravel

I'm having a problem when I try to import the data; in particular, I check if the email is already present; I'm use maatwebsite-excel
If it already exists I want to skip and move on. I tried to do this as the code below shows but once I load the data from an external sheet, where of course there are also clients with new emails, the data is not imported.
I don't get any errors but the data is not saved in the database.
ImportClass
namespace App\Imports;
use App\Models\Client;
use Maatwebsite\Excel\Row;
use Maatwebsite\Excel\Concerns\OnEachRow;
class ClientsImport implements OnEachRow
{
public function rules(): array
{
return [
'email' => 'required | unique:clients'
];
}
public function model(array $row)
{
return new Client([
'name' => $row[1],
'surname' => $row[2],
'email' => $row[3],
]);
}
}
Controller
public function importFile(Request $request)
{
Excel::import(new ClientsImport, $request->file('file')->store('temp'));
return back();
}
This is my code.

Related

excel sheet validation in laravel

I using laravel excel Maatwebsite.
public function collection(Collection $rows)
{
Validator::make($rows->toArray(), [
'*.price' => 'numeric',
])->validate();
}
I need output
Excel Row No not array number
Row No 1.price must be a number.
as I understand you need to validate uploaded excel rows.
the package excel Maatwebsite provided validation rules feature too.
in your import class you need to add WithValidation, WithHeadingRow interface and rules mothod. in this way uploaded excel will be validated before insert into database:
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Validators\Failure;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsFailures;
class UsersImport implements ToModel, WithValidation, WithHeadingRow,SkipsOnFailure
{
use Importable,SkipsFailures;
public function model(array $row)
{
return new User([
'name' => $row['name'],
'email' => $row['email'],
'password' => 'secret',
]);
}
public function rules(): array
{
return [
'email' => Rule::in(['patrick#maatwebsite.nl']),
// Above is alias for as it always validates in batches
'*.email' => Rule::in(['patrick#maatwebsite.nl']),
];
}
}
and to gather errors:
$import = new UsersImport();
$import->import('users.xlsx');
$failures= $import->failures() ;
foreach ($failures as $failure) {
$failure->row(); // row that went wrong
$failure->attribute(); // either heading key (if using heading row concern) or column index
$failure->errors(); // Actual error messages from Laravel validator
$failure->values(); // The values of the row that has failed.
}
now $failures contains all validation error in all rows
This example use interface Validator that take different arguments to initialize; (array $data, array $rules, array $messages, array $customAttributes) to validate rows and referenced from web sources, do not implement ToModel concern and is similar to the example showed in the question.
namespace App\Imports;
use App\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Concerns\ToCollection;
class UsersImport implements ToCollection
{
public function collection(Collection $rows)
{
Validator::make($rows->toArray(), [
'*.0' => 'required',
])->validate();
foreach ($rows as $row) {
User::create([
'name' => $row[0],
]);
}
}
}
Another scenario like defined a custom data or Excel file like the following, suppose you want to access specific cells, you can implement the WithMappedCells concern.
index
user
date
subscription
0
user
2022-12-08
true
1
user
2022-12-08
true
2
user
2022-12-08
false
3
user
2022-12-08
true
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithMappedCells;
class UsersImport implements WithMappedCells, ToModel
{
public function mapping(): array
{
return [
'index' => 'B1',
'user' => 'B2',
'date' => 'B3',
'subscription' => 'B4',
];
}
public function model(array $row)
{
return new User([
'index' => $row['index'],
'user' => $row['user'],
'date' => $row['date'],
'subscription' => $row['subscription']
]);
}
}

How to upload file in relationship hasOn<->belongsTo Laravel Backpack

Can be possible to store a file uploaded to a related table?
Scenario: I have a usres table in database and another one pictures. Users Model have the following function
public function picture()
{
return $this->hasOne(Picture::class);
}
And the Picture Model have the following function.
public function user_picture()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
Is possible to store the picture in pictures database table (id, user_id, img_path) from the UserCrudController store() function?
try something like this
public function store(Request $request)
{
Picture::create([
'user_id' => // get the user id from $request or auth()->user(),
'img_path' => $request->file('image')->store('images', 'public'),
]);
return // your view or something else
}
Let's say it is a registration form that need to insert an image. Instead of using the Picture model directly you can just do this :
public function store(Request $request)
{
$request->validate(...);
$user = User::create(...);
//It will ensure that the image belongs to the user.
$user->picture()->create([
'image_path' => $request->file('image')->store('images');
])
}
I resolved the issue with the following steps.
As per Laravel Backpack I added the input field in the Blade:
#include('crud::fields.upload', ['crud' => $crud, 'field' => ['name' => 'img1', 'label' => 'Image 1', 'type' => 'upload', 'upload'=> true, 'disk'=>'uploads', 'attributes' => ['id' => 'img1', 'capture' => 'user']]])
After this I added the function in the User Controller as follow:
$request->validate(['img1' => 'mimes:jpg,png,jpeg|max:5120']);
$fileModel = new Picture;
if($request->file()) {
$fileName1 = time().'_'.$request->img1->getClientOriginalName();
$filePath1 = $request->file('img1')->storeAs('uploads', $fileName1, 'public');
$fileModel->name = time().'_'.$request->img1->getClientOriginalName();
$fileModel->img1 = '/storage/' . $filePath1;
$fileModel->save();
}
With these lines of code I was able to store the related Picture with the User.
Thank you all for the guidelines.

Laravel: Automatically Create Slug from Title Using Create & Validate Methods

I want to automatically create a slug and save it to the database based on the title entered into a form. Currently, this is how my controller is set up:
public function store(News $id) {
News::create($this->validateArticle());
return redirect('/news');
}
public function validateArticle() {
return request()->validate([
'title' => 'required',
'excerpt' => 'nullable',
'body' => 'nullable'
]);
}
How can I modify this code so that I automatically generate a slug based off of the title?
Thanks.
This is another option of how to do it. Or you could use Observer to observe the crating method like so news->slug= Str::slug($request->title);
public function store(Request $request)
{
$news= new News();
$news->title= $request->title;
$news->slug= Str::slug($request->title);
$news->excerpt= $request->excerpt;
$news->body= $request->body;
$news->save();
return redirect('/news');
}
Make use you import Str use Illuminate\Support\Str;

Find data before validate form request laravel

I want to update the data using the request form validation with a unique email role, everything works normally.
Assume I have 3 data from id 1-3 with url:
127.0.0.1:8000/api/user/update/3
Controller:
use App\Http\Requests\Simak\User\Update;
...
public function update(Update $request, $id)
{
try {
// UPDATE DATA
return resp(200, trans('general.message.200'), true);
} catch (\Exception $e) {
// Ambil error
return $e;
}
}
FormRequest "Update":
public function rules()
{
return [
'user_akses_id' => 'required|numeric',
'nama' => 'required|max:50',
'email' => 'required|email|unique:users,email,' . $this->id,
'password' => 'required',
'foto' => 'nullable|image|max:1024|mimes:jpg,png,jpeg',
'ip' => 'nullable|ip',
'status' => 'required|boolean'
];
}
but if the updated id is not found eg:
127.0.0.1:8000/api/user/update/4
The response gets The email has already been taken.
What is the solution so that the return of the data is not found instead of validation first?
The code looks like it should work fine, sharing a few things below that may help.
Solution 1: Check if $this->id contains the id you are updating for.
Solution 2: Try using the following changes, try to get the id from the URL segment.
public function rules()
{
return [
'user_akses_id' => 'required|numeric',
'nama' => 'required|max:50',
'email' => 'required|email|unique:users,email,' . $this->segment(4),
'password' => 'required',
'foto' => 'nullable|image|max:1024|mimes:jpg,png,jpeg',
'ip' => 'nullable|ip',
'status' => 'required|boolean'
];
}
Sharing one more thing that may help you.
Some person uses Request keyword at the end of the request name. The Update sounds generic and the same as the method name you are using the request for. You can use UpdateRequest for more code readability.
What I understand from your question is, you need a way to check if the record really exists or not in the form request. If that's the case create a custom rule that will check if the record exists or not and use that rule inside your request.
CheckRecordRule
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CheckRecordRule implements Rule
{
protected $recordId;
public function __construct($id)
{
$this->recordId = $id;
}
public function passes($attribute, $value)
{
// this will check and return true/false
return User::where('id', $this->recordId)->exists();
}
public function message()
{
return 'Record not found.';
}
}
Update form request
public function rules()
{
return [
'email' => 'required|email|unique:users,email,' . $this->id.'|'. new CheckRecordRule($this->id),
];
}
So when checking for duplicate it will also check if the record really exists or not and then redirect back with the proper message.

Laravel faking file in the factory not working

I am developing a Web application using Laravel. I am unit testing my application that involves file operation. Have a look at my scenario below.
I have a download file action in the controller like this
public function downloadPersonalDetails(Order $order)
{
if (! auth()->user()->can('downloadPersonalDetails', $order)) {
abort(401);
}
return Storage::download($order->path);
}
I have the factory like this OrderFactory.php
$factory->define(Application::class, function (Faker $faker) {
return [
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'email' => $faker->email,
//other fields ....
'path' => $faker->file('public/assets/files'),
});
This is my unit test to that download action
public function test_download_personal_details_file()
{
$user = //created a authorized valid user
$order = factory(Order::class)
->create([
'user' => $user->id
]);
$this->actingAs($user)->get(route('order.personal_info.download', $order))->assertStatus(200);
}
When I run the test I am getting error saying file does not exist.
File not found at path: tmp/439fe03f-f1c7-3b84-b123-d627d0395bd8.pdf
Why is that not working? How can I fix it and what is wrong with my code.
The faker will just create a fake filename however that filename will not correspond to any actual file. You will need to also fake it in the storage:
public function test_download_personal_details_file()
{
$user = //created a authorized valid user
$order = factory(Order::class)
->create([
'user' => $user->id
]);
\Storage::fake(config('filesystems.default')); //or any storage you need to fake
\Storage::put($order->path, ''); //Empty file
$this->actingAs($user)->get(route('order.personal_info.download', $order))->assertStatus(200);
}
This will create a file in the fake storage (so it won't actually write to disk).
This requires Laravel 5.4+

Resources