Laravel : Overwrite Socialite Provider to add new fields - laravel

I want to extend/overwrite my LinkedInProvider.php (in vendor\laravel\socialite\src\Two) to add new fields in the Linkedin Request.
I've create a new LinkedInProvider.php (in app\Providers) with the following code :
namespace App\Providers;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\ProviderInterface;
use Laravel\Socialite\Two\User;
class LinkedInProvider extends AbstractProvider implements ProviderInterface
{
/**
* The scopes being requested.
*
* #var array
*/
protected $scopes = ['r_basicprofile', 'r_emailaddress'];
/**
* The separating character for the requested scopes.
*
* #var string
*/
protected $scopeSeparator = ' ';
/**
* The fields that are included in the profile.
*
* #var array
*/
protected $fields = [
'id', 'first-name', 'last-name', 'formatted-name',
'email-address', 'headline', 'location', 'industry', 'positions',
'public-profile-url', 'picture-url', 'picture-urls::(original)',
];
/**
* {#inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state);
}
/**
* {#inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.linkedin.com/oauth/v2/accessToken';
}
/**
* Get the POST fields for the token request.
*
* #param string $code
* #return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* {#inheritdoc}
*/
protected function getUserByToken($token)
{
$fields = implode(',', $this->fields);
$url = 'https://api.linkedin.com/v1/people/~:('.$fields.')';
$response = $this->getHttpClient()->get($url, [
'headers' => [
'x-li-format' => 'json',
'Authorization' => 'Bearer '.$token,
],
]);
return json_decode($response->getBody(), true);
}
/**
* {#inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['id'], 'nickname' => null, 'name' => Arr::get($user, 'formattedName'),
'email' => Arr::get($user, 'emailAddress'), 'avatar' => Arr::get($user, 'pictureUrl'),
'avatar_original' => Arr::get($user, 'pictureUrls.values.0'),
]);
}
/**
* Set the user fields to request from LinkedIn.
*
* #param array $fields
* #return $this
*/
public function fields(array $fields)
{
$this->fields = $fields;
return $this;
}
}
But now, I've got this error :
Type error: Argument 1 passed to Laravel\Socialite\Two\AbstractProvider::__construct() must be an instance of Illuminate\Http\Request, instance of Illuminate\Foundation\Application given, called in G:\laragon\www\localhost\vendor\laravel\framework\src\Illuminate\Foundation\ProviderRepository.php on line 201
I know I can install Socialite Manager, but I just want to overwrite the fields list to add new field (like position and industry)

You shouldn't have to overwrite/extend the whole class. In the Laravel\Socialite\Two\User object that is being created, there is a $user property, which contains the raw information the provider sent back.
When making the request, you can set the fields you want LinkedIn to return in your controller method:
public function redirectToProvider()
{
$fields = [
'id', 'first-name', 'last-name', 'formatted-name',
'email-address', 'headline', 'location', 'industry',
'public-profile-url', 'picture-url', 'picture-urls:(original)',
'positions', 'summary' // <-- additional fields here
];
return Socialite::driver('linkedin')->fields($fields)->redirect();
}
You can see two additional fields being requested, positions and summary, which aren't included by default.
Happy hacking!

Related

The email has already been taken [duplicate]

This question already has answers here:
Laravel: Validation unique on update
(33 answers)
Closed 10 months ago.
I'm going to edit a row using form request validate laravel, but when editing if I do not change the email, The email has already been taken error. Gives
this is from request validate EditAdmin
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules;
use Illuminate\Validation\Rule;
class EditAdmin 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 [
'name' => ['required', 'string'],
'email' => ['required', 'string', 'email', Rule::unique('admins')],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
];
}
}
this my controller AdminController
<?php
namespace App\Http\Controllers;
use App\Repositories\AdminRepository;
use App\Http\Requests\CreateAdmin;
use App\Interfaces\AdminInterface;
use App\Http\Requests\EditAdmin;
use Illuminate\Validation\Rules;
use Illuminate\Validation\Rule;
use Illuminate\Http\Request;
use App\Models\Admin;
class AdminController extends Controller
{
/**
* admin Service Interface
* #var AdminRepository
*/
protected $admin;
/**
* Inject service By Service Container
* #param AdminRepository $admin
*/
public function __construct(AdminRepository $admin)
{
$this->admin = $admin;
}
/**
* Show All Admins
* Method:get
* Return get All admin
* #return \Illuminate\Contracts\View\View
*/
public function getAllAdmin():\Illuminate\Contracts\View\View
{
$admins = $this->admin->getAllAdmin();
return view('admin.list',['admins'=>$admins]);
}
/**
* param:Admin_id
* method delete
* Delete admin
* #param int $id
* #return \Illuminate\Http\RedirectResponse
*/
public function deleteAdmin(int $id):\Illuminate\Http\RedirectResponse
{
$this->admin->deleteAdmin($id);
return redirect('/admins');
}
/**
* get single admin
* method get
* #param $id
* #return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
*/
public function getAdmin(int $id):\Illuminate\Contracts\View\View
{
$admin = $this->admin->getAdmin($id);
return view('admin.edit',['admin'=>$admin]);
}
/**
* Updated Admin
* Route:Api/Nft/$nft_id
* Method:Put
* #param Request $request
* #return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function editAdmin(EditAdmin $request):\Illuminate\Http\RedirectResponse
{
$request->validated();
//validate all input
// $request->validate([
// 'name' => ['required', 'string'],
// 'email' => ['required', 'string', 'email', Rule::unique('admins')->ignore($request->id),],
// 'password' => ['required', 'confirmed', Rules\Password::defaults()],
// ]);
$data = $request->all();
$this->admin->editAdmin($data);
return redirect('/admins');
}
/**
* create new admin
* #param CreateAdmin $request
* #return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function createAdmin(CreateAdmin $request):\Illuminate\Http\RedirectResponse
{
$request->validated();
$data = $request->all();
$this->admin->createAdmin($data);
return redirect('/admins');
}
}
How can I fix this bug?
pleas help me!!!
this is because of using same validator for crate and update user . and user own email in update process make trouble for you .
solotion:
1- crate new validator class for edit user << UserEditRequest >>
2- use this code for validate email
'email' => ['required', 'string', 'email,'.auth()->user()->id,]

I have an idea to use auth middleware to retrieve a user's products

i have an idea to use auth middleware to retrieve a user's products but currently i am not able to retrieve that user's products. Instead, it listed all the products of other users. Can you guys give me some ideas to help me complete it. Thank you !!!
This is my ProjectController code:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Resources\ProjectResource;
use App\Models\Project;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ProjectController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$projects = Project::all();
return response([
'projects' => ProjectResource::collection($projects),
'message' => 'Retrieved successfully'
], 200);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$data = $request->all();
$validator = Validator::make($data, [
// 'uuid' => 'required|unique:projects',
'user_id' => 'required',
'name' => 'required|max:255',
'status' => 'required',
]);
if ($validator->fails()) {
return response(['error' => $validator->errors(), 'Validation Error']);
}
$projects = Project::create($data);
return response(['projects' => new ProjectResource($projects), 'message' => 'Created successfully'], 201);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show(Project $project)
{
return response(['project' => new ProjectResource($project), 'message' => 'Retrieved successfully'], 200);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, Project $project)
{
$project->update($request->all());
return response(['project' => new ProjectResource($project), 'message' => 'Update successfully'], 200);
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy(Project $project)
{
$project->delete();
return response(['message' => 'Deleted'], 204);
}
}
This is my ProjectModel code:
<?php
namespace App\Models;
use App\Enums\ProjectStatus;
use App\Http\Traits\Uuid;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Project extends Model
{
use HasFactory;
use Uuid;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'status',
'user_id',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'id',
'uuid',
];
protected $cast = [
'status' => ProjectStatus::class
];
public function setUuidAttribute()
{
$this->attributes['uuid'] = Str::uuid();
}
}
This is my API Router code:
You have to retrieve the user first in order to filter the products. There are multiple ways to do that. Here's a link to Laravel's documentation on the subject: https://laravel.com/docs/8.x/authentication#retrieving-the-authenticated-user
You also have to filter your query (your query being Project::all()) so that it only retrieves the products for the user. Here's a link to some Laravel documentation related to that: https://laravel.com/docs/8.x/eloquent#retrieving-models
In the end, replacing this $projects = Project::all(); with this $projects = Project::where('user_id', auth()->user()->id); should do the trick.
Try this
public function index()
{
$projects = Project::where('user_id', auth()->id())->get();
return response([
'projects' => ProjectResource::collection($projects),
'message' => 'Retrieved successfully'
], 200);
}

How to implement many to many nova resource without building custom tool

I am currently building a timetable generation system, I have these models below which are Subject and Teacher as the two main models with their nova resources, I have created a pivot model SubjectAllocation (has a nova resource) with a pivot table subject_allocations with fields teacher_id and subject_id. I would like to be able to use SubjectAllocation nova resource to select a teacher and allocate multiple subjects to this teacher but currently, I have no lack of it. Tried pulling in this package dillingham/nova-attach-many to attach to the SubjectAllocation model and this package to pick teachers records sloveniangooner/searchable-select but it cannot store data in the pivot table.
Subject Allocation Resource
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\BelongsToMany;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Http\Requests\NovaRequest;
use NovaAttachMany\AttachMany;
use Sloveniangooner\SearchableSelect\SearchableSelect;
class SubjectAllocation extends Resource
{
/**
* The model the resource corresponds to.
*
* #var string
*/
public static $model = 'App\SubjectAllocation';
/**
* The single value that should be used to represent the resource when being displayed.
*
* #var string
*/
public static $title = 'id';
/**
* The columns that should be searched.
*
* #var array
*/
public static $search = [
'id',
];
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [
ID::make()->sortable(),
SearchableSelect::make('Teacher', 'teacher_id')->resource("teachers"),
AttachMany::make('Subjects')
->showCounts()
->help('<b>Tip: </b> Select subjects to be allocated to the teacher'),
];
}
/**
* Get the cards available for the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function cards(Request $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function filters(Request $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function lenses(Request $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function actions(Request $request)
{
return [];
}
}
Fields Methods in Subject Resource
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Subject Name', 'name')
->withMeta(['extraAttributes' => ['placeholder' => 'Subject Name']])
->sortable()
->creationRules('required', 'max:255', 'unique:subjects,name')
->updateRules('required', 'max:255'),
Text::make('Subject Code', 'code')
->withMeta(['extraAttributes' => ['placeholder' => 'Subject Code']])
->sortable()
->creationRules('required', 'max:255', 'unique:subjects,code')
->updateRules('required', 'max:255')
,
Textarea::make('Description')
->nullable(),
BelongsToMany::make('Teachers'),
];
}
Fields method in Teacher Resource
public function fields(Request $request)
{
return [
ID::make()->sortable(),
BelongsTo::make('User')
->searchable(),
Text::make('First Name', 'first_name')
->withMeta(['extraAttributes' => ['placeholder' => 'First Name']])
->sortable()
->rules('required', 'max:50'),
Text::make('Middle Name', 'middle_name')
->withMeta(['extraAttributes' => ['placeholder' => 'Middle Name']])
->sortable()
->nullable()
->rules('max:50'),
Text::make('Last Name', 'last_name')
->withMeta(['extraAttributes' => ['placeholder' => 'Last Name']])
->sortable()
->rules('required', 'max:50'),
Text::make('Teacher Code', 'teacher_code')
->withMeta(['exraAttributes' => [ 'placeholder' => 'Teacher Code']])
->sortable()
->creationRules('required', 'max:50', 'unique:teachers,teacher_code')
->updateRules('required', 'max:50'),
BelongsToMany::make('Subjects'),
];
}
Any suggestion on how I can make it work or a better solution, would appreciate very much
Without build custom tool, use following method:
// app\Nova\SubjectAllocation.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
// SearchableSelect::make('Teacher', 'teacher_id')->resource("teachers"),
SearchableSelect::make('Teacher', 'teacher_id')->resource(\App\Nova\Teacher::class)
->displayUsingLabels(),
AttachMany::make('Subjects','subject_id')
->showCounts()
->help('<b>Tip: </b> Select subjects to be allocated to the teacher')
->fillUsing(function($request, $model, $attribute, $requestAttribute) {
$a = json_decode($request->subject_id, true);
$teacher = \App\Teacher::find($request->teacher_id);
if(count($a)==0){
// Error processing because no subject is choosen
}else if(count($a)==1){
$model['subject_id'] = $a[0];
}else{
$model['subject_id'] = $a[0];
array_shift ($a); // Remove $a[0] in $a
$teacher->subjects()->sync(
$a
);
}
})
];
}

Laravel Datatable only searchs ID

I generated my Laravel CRUD views with InfyOm, which uses Datatables to present it. It comes with a search field by default, but when I try to search it only searches by ID. I can't find how I can set to search in the other fields too. I can't find any documentation about how to implement this. It seems that it has something to do with this, but I didn't get it. How can I make this?
Here is my Datatable class:
<?php
namespace App\DataTables;
use App\Models\SubscriptionStatus;
use Yajra\DataTables\Services\DataTable;
use Yajra\DataTables\EloquentDataTable;
class SubscriptionStatusDataTable extends DataTable
{
/**
* Build DataTable class.
*
* #param mixed $query Results from query() method.
* #return \Yajra\DataTables\DataTableAbstract
*/
public function dataTable($query)
{
$dataTable = new EloquentDataTable($query);
return $dataTable->addColumn('action', 'subscription_statuses.datatables_actions');
}
/**
* Get query source of dataTable.
*
* #param \App\Models\Post $model
* #return \Illuminate\Database\Eloquent\Builder
*/
public function query(SubscriptionStatus $model)
{
return $model->newQuery();
}
/**
* Optional method if you want to use html builder.
*
* #return \Yajra\DataTables\Html\Builder
*/
public function html()
{
return $this->builder()
->columns($this->getColumns())
->minifiedAjax()
->addAction(['width' => '80px', "title" => "Ações"])
->parameters([
'dom' => 'Bfrtip',
'order' => [[0, 'desc']],
'buttons' => [
'export',
'print',
'reset',
'reload',
],
]);
}
/**
* Get columns.
*
* #return array
*/
protected function getColumns()
{
return [
['data' => 'name', 'title' => 'Nome'],
['data' => 'description', 'title' => 'Descrição'],
['data' => 'expire_days', 'title' => 'Tempo para Expirar'],
];
}
/**
* Get filename for export.
*
* #return string
*/
protected function filename()
{
return 'subscription_statusesdatatable_' . time();
}
}

Laravel 5 FormRequest validator with multiple scenarios

I would like to ask how should I handle validation on multiple scenarios using FormRequest in L5? I know and I was told that I can create saparate FormRequest files to handle different validations but it is very redundant and also noted that I would need to inject it into the controller manually using the use FormRequest; keyword. What did previously in L4.2 is that I can define a new function inside my customValidator.php which then being called during controller validation via trycatch and then the data is being validated by service using the below implementation.
class somethingFormValidator extends \Core\Validators\LaravelValidator
{
protected $rules = array(
'title' => 'required',
'fullname' => 'required',
// and many more
);
public function scenario($scene)
{
switch ($scene) {
case 'update':
$this->rules = array(
'title' => 'required',
'fullname' => 'required',
// and other update validated inputs
break;
}
return $this;
}
}
Which then in my LaravelValidator.php
<?php namespace Core\Validators;
use Validator;
abstract class LaravelValidator {
/**
* Validator
*
* #var \Illuminate\Validation\Factory
*/
protected $validator;
/**
* Validation data key => value array
*
* #var Array
*/
protected $data = array();
/**
* Validation errors
*
* #var Array
*/
protected $errors = array();
/**
* Validation rules
*
* #var Array
*/
protected $rules = array();
/**
* Custom validation messages
*
* #var Array
*/
protected $messages = array();
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
/**
* Set data to validate
*
* #return \Services\Validations\AbstractLaravelValidator
*/
public function with(array $data)
{
$this->data = $data;
return $this;
}
/**
* Validation passes or fails
*
* #return Boolean
*/
public function passes()
{
$validator = Validator::make(
$this->data,
$this->rules,
$this->messages
);
if ($validator->fails())
{
$this->errors = $validator->messages();
return false;
}
return true;
}
/**
* Return errors, if any
*
* #return array
*/
public function errors()
{
return $this->errors;
}
}
and then finally this is how i call the scenarios inside services like this
public function __construct(somethingFormValidator $v)
{
$this->v = $v;
}
public function updateSomething($array)
{
if($this->v->scenario('update')->with($array)->passes())
{
//do something
else
{
throw new ValidationFailedException(
'Validation Fail',
null,
$this->v->errors()
);
}
}
So the problem is now since i have migrated to L5 and L5 uses FormRequest, how should I use scenario validation in my codes?
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class ResetpasswordRequest extends Request {
/**
* 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 [
'login_email' => 'required',
'g-recaptcha-response' => 'required|captcha',
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'g-recaptcha-response.required' => 'Are you a robot?',
'g-recaptcha-response.captcha' => 'Captcha session timeout'
];
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = array(
//scenario rules
);
break;
}
return $this;
}
}
also how should I call it in the controller?
public function postReset(ResetpasswordRequest $request)
{
$profile = ProfileService::getProfileByEmail(Request::input('login_email'));
if($profile == null)
{
$e = array('login_email' => 'This email address is not registered');
return redirect()->route('reset')->withInput()->withErrors($e);
}
else
{
//$hash = ProfileService::createResetHash($profile->profile_id);
$time = strtotime('now');
$ip = Determinator::getClientIP();
MailProcessor::sendResetEmail(array('email' => $profile->email,
'ip' => $ip, 'time' => $time,));
}
}
I believe the real issue at hand is everything is validated through the form request object before it reaches your controller and you were unable to set the appropriate validation rules.
The best solution I can come up with for that is to set the validation rules in the form request object's constructor. Unfortunately, I am not sure how or where you are able to come up with the $scene var as it seems to be hard-coded in your example as 'update'.
I did come up with this though. Hopefully reading my comments in the constructor will help further.
namespace App\Http\Requests;
use App\Http\Requests\Request;
class TestFormRequest extends Request
{
protected $rules = [
'title' => 'required',
'fullname' => 'required',
// and many more
];
public function __construct()
{
call_user_func_array(array($this, 'parent::__construct'), func_get_args());
// Not sure how to come up with the scenario. It would be easiest to add/set a hidden form field
// and set it to 'scene1' etc...
$this->scenario($this->get('scenario'));
// Could also inspect the route to set the correct scenario if that would be helpful?
// $this->route()->getUri();
}
/**
* 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 $this->rules;
}
public function scenario($scene)
{
switch ($scene) {
case 'scene1':
$this->rules = [
//scenario rules
];
break;
}
}
}
You can use laratalks/validator package for validation with multiple scenarios in laravel. see this repo

Resources