POST http://localhost:8000/api/projects/ 500 (Internal Server Error) - php

I'm working on a rest api project, I'm using Laravel and Angular. To resume, my project is like a project manager. I've got 4 tables: user, project, project_user and status. So when a user create a project, he writes just the name and the project is created. The creator_id is the current user id. For the backend, I'm using Laravel JWT for the auth. And this is the code of the controller to create a project:
Project model:
class Project extends Model
{
protected $fillable = [
'name', 'creator_id', 'status_id'
];
protected $hidden = [
'created_at', 'updated_at'
];
protected $table = 'projects';
public function users()
{
return $this->belongsToMany(User::class)->select( 'email', 'date_of_birth', 'firstname', 'lastname');
}
public function status()
{
return $this->belongsTo(Status::class);
}
}
So the route in backend works, I tried with Postman and all is good. But I've got a problem with this route to create a project in frontend. It doesn't work and I've got the error 500 (Internal Server Error). When I inspect the web page, in security, I've got this message:
This is my angular code:
export class AddProjectComponent implements OnInit {
project = {
name: '',
};
submitted = false;
constructor(private projectService: ProjectService) { }
ngOnInit() {
}
createProject(): void {
const data = {
name: this.project.name,
};
this.projectService.create(data)
.subscribe(
response => {
console.log(response);
this.submitted = true;
},
error => {
console.log(error);
}
);
}
newProject(): void {
this.submitted = false;
this.project = {
name: ''
};
}
}
project.service.ts
create(data): Observable<any>{
return this.http.post(`http://localhost:8000/api/projects/`, data);
}
I don't know why it doesn't work because my user is well connected, i've got his auth_token in the local storage.
Someone can help me pls ? ^^
I think I have put the most useful files but if they are missing I can add them to you.
Thanks in advance

Issue is with $request-request->add(['creator_id'=>Auth::id()])
instead of that use request merge.
$request->merge(['creator_id'=>Auth::id()])

Your answer is right there in the error message. creator_id does not have a default value which means you HAVE to pass a value to it for it to create the row in the DB

Related

Why do api routes in tenant.php with Laravel VueJs are working with 200 OK status code in network tools but I'm receiving a blade as a response?

First of all, I'd like to comment on this that I'm working on a project based on Laravel VueJs with tenancy for laravel v3 package.
To get into context, I can perfectly log in on my name#example.com account registered at any created tenant on local development but after logging in, I'm struggling mainly with TypeError: Cannot read properties of undefined (reading 'xxxxx') in console with all the api-related and VueJs routes. I have been digging deeper over the problem and got into a conclusion.
TypeError: Cannot read properties of undefined (reading 'xxxxx')
And with that, I figured out my GET route with GetUserAuth as uri at 'api' middleware on tenant.php is recognized but isn't calling the method associated to it but throwing as a status code 200 OK. Instead, the response is a blade as in the following picture.
Where Respuesta is Response
It's also important to mention I've set up Laravel Passport according to the stancl/tenancy v3 documentation, specifically using Passport in the tenant application only with shared passport encryption keys.
The following code refers to my tenant.php
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
use Stancl\Tenancy\Middleware\PreventAccessFromCentralDomains;
use App\Http\Controllers\UserController;
/*
|--------------------------------------------------------------------------
| Tenant Routes
|--------------------------------------------------------------------------
|
| Here you can register the tenant routes for your application.
| These routes are loaded by the TenantRouteServiceProvider.
|
| Feel free to customize them however you want. Good luck!
|
*/
Route::middleware(['api', InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class])->group(
function ()
{
/*auth middleware api passport token*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
// Other routes
Route::middleware(['auth:api', 'Is_Active'])->group(function ()
{
//Other routes
//------------------------------- Users --------------------------\\
Route::get('GetUserRole', [UserController::class, "GetUserRole"]);
Route::get('GetUserAuth', [UserController::class, "GetUserAuth"]);
Route::get("/GetPermissions", [UserController::class, "GetPermissions"]);
Route::resource('users', UserController::class);
Route::put('users/Activated/{id}', [UserController::class, "IsActivated"]);
Route::get('users/export/Excel', [UserController::class, "exportExcel"]);
Route::get('users/Get_Info/Profile', [UserController::class, "GetInfoProfile"]);
Route::put('updateProfile/{id}', [UserController::class, "updateProfile"]);
});
});
Route::middleware(['web', InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class])->group(
function ()
{
// Web routes
});
The following one is referring to the code located at C:/project-root/resources/src/store/modules/auth.js
import Vue from 'vue'
import Vuex from 'vuex'
// import VueCookie from 'vue-cookie'
import axios from 'axios'
import router from "./../../router";
Vue.use(Vuex)
// Vue.use(VueCookie)
const state = {
// token: Vue.cookie.get('Stocky_token'),
isAuthenticated:false,
Permissions: null,
user: {},
loading: false,
error: null,
notifs:0,
};
const getters = {
isAuthenticated: state => state.isAuthenticated,
currentUser: state => state.user,
currentUserPermissions: state => state.Permissions,
loading: state => state.loading,
notifs_alert: state => state.notifs,
error: state => state.error
};
const mutations = {
setLoading(state, data) {
state.loading = data;
state.error = null;
},
setError(state, data) {
state.error = data;
state.loggedInUser = null;
state.loading = false;
},
clearError(state) {
state.error = null;
},
// setLoginCred(state, payload) {
// state.token = payload.token;
// // state.isAuthenticated = true;
// },
setPermissions(state, Permissions) {
state.Permissions = Permissions;
// state.user = user;
},
setUser(state, user) {
state.user = user;
},
// SET_AUTHENTICATED(state, isAuthenticated) {
// state.isAuthenticated = isAuthenticated;
// },
Notifs_alert(state, notifs) {
state.notifs = notifs;
},
logout(state) {
// state.token = null;
state.user = null;
state.Permissions = null;
// state.isAuthenticated = false;
// Vue.cookie.delete('Stocky_token');
state.loggedInUser = null;
state.loading = false;
state.error = null;
},
};
const actions = {
// setLoginCred(context, payload) {
// context.commit('setLoading', true)
// context.commit('setLoginCred', payload)
// },
async refreshUserPermissions(context) {
await axios.get("GetUserAuth").then((userAuth) => {
let Permissions = userAuth.data.permissions
let user = userAuth.data.user
let notifs = userAuth.data.notifs
// context.commit('SET_AUTHENTICATED', true)
context.commit('setPermissions', Permissions)
context.commit('setUser', user)
context.commit('Notifs_alert', notifs)
}).catch(() => {
// context.commit('SET_AUTHENTICATED', false)
context.commit('setPermissions', null)
context.commit('setUser', null)
context.commit('Notifs_alert', null)
});
},
logout({ commit }) {
axios({method:'post', url: '/logout', baseURL: '' })
.then((userData) => {
window.location.href='/login';
})
},
};
export default {
state,
getters,
actions,
mutations
};
And the following one refers to the mentioned before method on UserCon related with the GET route throwing status code 200 OK with a blade as a response
//------------- GET USER Auth ---------\\
public function GetUserAuth(Request $request)
{
$helpers = new helpers();
$user['avatar'] = Auth::user()->avatar;
$user['username'] = Auth::user()->username;
$user['currency'] = $helpers->Get_Currency();
$user['logo'] = Setting::first()->logo;
$user['footer'] = Setting::first()->footer;
$user['developed_by'] = Setting::first()->developed_by;
$user['initCCF'] = Auth::user()->initCCF;
$user['currentCCF'] = Auth::user()->currentCCF;
$user['finalCCF'] = Auth::user()->finalCCF;
$user['initCF'] = Auth::user()->initCF;
$user['currentCF'] = Auth::user()->currentCF;
$user['finalCF'] = Auth::user()->finalCF;
$user['warehouse_id'] = Auth::user()->warehouse_id;
$permissions = Auth::user()->roles()->first()->permissions->pluck('name');
$products_alerts = product_warehouse::join('products', 'product_warehouse.product_id', '=', 'products.id')
->whereRaw('qte <= stock_alert')
->where('product_warehouse.deleted_at', null)
->count();
return response()->json([
'success' => true,
'user' => $user,
'notifs' => $products_alerts,
'permissions' => $permissions,
]);
}
Finally, I'd like to mention that my application with central domain only and a single database was working perfectly but I don't know what could be the problem even debugging, checking all the documentation related and other answered questions but none of those fixed my issue and I need help as soon as possible. Any suggestions or help are welcomed and if you need extra information, please let me know. Thanks in advance for taking your time.
A common thing I faced when using tenancy for laravel is the default Authenticate middleware included with the SaaS boilderplate you can purchase. It extends the default laravel Authenticate and adds this code:
if (! $request->expectsJson()) {
// return a blade view
return route('tenant.login');
}
This redirects all requests to the tenant.login view if the Accept: application/json header is missing.
I can't really see the headers of the request you're seding to GetUserAuth, but this could be the reason.

$user null but not null on json login

I have a very weird behavior on symfony 6 with json login. Json login is configured like in their example.
I added the most simple Cors-handling
class CorsSubscriber implements EventSubscriberInterface
{
public function onKernelResponse(ResponseEvent $event)
{
$response = $event->getResponse();
$response->headers->add([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Headers' => 'Content-Type'
]);
}
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => 'onKernelResponse',
];
}
}
Login Controller
class LoginController extends AbstractController
{
#[Route('/login', name: 'api_login')]
public function login(#[CurrentUser] ?User $user): JsonResponse
{
if ($user === null) {
$this->json([
'message' => 'Not found',
], JsonResponse::HTTP_UNAUTHORIZED);
}
$token = "token"; // some token logik follows
$response = new JsonResponse([
'user' => $user->getUserIdentifier(),
'token' => $token,
]);
return $response;
}
}
Again, basically straight out of their tutorial.
DB is also set up and a user was added beforehand. If I now want to login via a Rest App (I use Insomnia) everything works as expected. I get the user identifier (email) and token back.
But when I want to do the exact same thing via an Angular web app, I get the following message in the log: [critical] Uncaught Error: Call to a member function getUserIdentifier() on null
let data = {username: "test#test.de", password: "test123"};
this.http
.post('http://localhost:8000/login', data)
.subscribe({
next: (value) => {
console.log(value);
},
});
Funny thing since I check if $user is null...
But the most funny thing is, if I change the $user->getUserIdentifier() call to just "$user" - it works and I get the whole User object returned (everything I defined in json_serializable)
Again, getUserIdentifier is from symfony itself
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
What am I missing?
In the documentation check_path: api_login is defined in the "security.yaml ".
In the ApiLoginController #[route('/api/login', name:'api_login')]
I wanted to change this to the path "/login".
Thereby I defined check_path: login in security.yaml
and in the controller #[route('/login', name:'api_login')]
However, the name must also be "login", like #[route('/login', name:'login')]
Found out what's wrong.
No return
if ($user === null) {
$this->json([
'message' => 'Not found',
], JsonResponse::HTTP_UNAUTHORIZED);
}
I missed the return before $this->json, allowing the call to proceed even while $user was indeed null.
Preflight Cors
I honestly don't have too much experience and knowledge about cors and wasn't really aware about the preflight calls being done. But that was the problem.
public function onKernelRequest(RequestEvent $event)
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
$method = $request->getRealMethod();
if ('OPTIONS' == $method) {
$response = new Response();
$event->setResponse($response);
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => ['onKernelResponse', 9999],
KernelEvents::REQUEST => ['onKernelRequest', 9999]
];
}
I extended the CorsSubscriber to check for Method OPTION if it is not the main request and just sending a 200 to pass the preflight.
This helped me understand the logic behind it:
https://gist.github.com/rordi/42de5982e877e1177a0e6a98ee8415ea
https://www.upbeatproductions.com/blog/cors-pre-flight-requests-and-headers-symfony-httpkernel-component

Laravel event with socket-io [receive notifications]

For the first time attempt with laravel and socket-io I am trying to send very simple notification to admins. So far my event is firing but I need help with receiving event notifications.
Logic
It's very basic because I want to understand the process.
User opens page Add Product
Admin gets notification that user X is in App Product page.
So far
So far I can fire event and get user data (user that is in Add Product page)
Need help for
I need help to understand the way that admin receives notifications.
Code
Component script
created() {
let user = JSON.parse(localStorage.getItem("user"))
this.listenForBroadcast(user);
},
methods: {
listenForBroadcast(user) {
Echo.join('userInAddProduct')
.here((Loggeduser) => {
console.log('My user data', Loggeduser);
});
}
}
Result of code above
My user data [{…}]
0:
id: 1
name: "Test User"
photo: "User-1588137335.png"
__ob__: Observer {value: {…}, dep: Dep, vmCount: 0}
get id: ƒ reactiveGetter()
set id: ƒ reactiveSetter(newVal)
get name: ƒ reactiveGetter()
set name: ƒ reactiveSetter(newVal)
get photo: ƒ reactiveGetter()
set photo: ƒ reactiveSetter(newVal)
__proto__: Object
length: 1
__proto__: Array(0)
Channels route
Broadcast::channel('userInAddProduct', function ($user) {
return [
'id' => $user->id,
'photo' => $user->photo,
'name' => $user->name
];
});
MessagePushed (event file)
class MessagePushed implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function broadcastOn()
{
return new PresenceChannel('userInAddProduct');
}
}
Question
How can I receive notification about this event fire? I want to notify my admin users that user x is in page Add Product?
Update
Since I published this question I've made some changes and here is my latest code + questions.
bootstrap.js
window.io = require('socket.io-client');
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
auth: { // added authentication token (because all my events are private)
headers: {
Authorization: localStorage.getItem('access_token'),
},
},
});
Add.vue (add product component where event has to be fired)
listenForBroadcast(user) {
let ui = JSON.parse(localStorage.getItem("user"))
Echo.join('userInAddProduct')
.here((users) => {
console.log('My user data', users)
})
.joining((user) => {
this.$notify({
title: '',
message: user + 'joining',
offset: 100,
type: 'success'
});
})
.leaving((user) => {
this.$notify({
title: '',
message: user + 'is leaving new product',
offset: 100,
type: 'warning'
});
})
.whisper('typing', (e) => {
this.$notify({
title: '',
message: ui.username + 'is adding new product',
offset: 100,
type: 'success'
})
})
.listenForWhisper('typing', (e) => {
console.log(e)
this.$notify({
title: '',
message: ui.username + 'is entered add new product page.',
offset: 100,
type: 'success'
});
})
.notification((notification) => {
console.log('noitication listener: ', notification.type);
});
},
Then I've made 4 files to handle events:
Event (passing data)
Event Listener (process the database storage and showing notifications to online admins)
Observer (firing event)
Notification (store data to database for admins in case when event fire they're not online so they can see notifications later on)
Event file
class MessagePushed extends Event implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
public $product;
public function __construct(User $user, Product $product)
{
$this->user = $user;
$this->product = $product;
}
public function broadcastOn()
{
return new PresenceChannel('userInAddProduct');
}
}
Listener file
class ThingToDoAfterEventWasFired implements ShouldQueue
{
public function handle(MessagePushed $event)
{
//Log testing purpose only
$user = $event->user->username;
$product = $event->product->name;
// Real data that should be broadcasts
$user2 = $event->user;
$product2 = $event->product;
// inform all admins and authorized staffs about new product
$admins = User::role(['admin', 'staff'])->get();
foreach($admins as $admin) {
$admin->notify(new UserAddProduct($user2, $product2));
}
Log::info("Product $product was Created, by worker: $user");
}
}
Notification
class UserAddProduct extends Notification implements ShouldQueue
{
use Queueable;
protected $product;
protected $user;
public function __construct(User $user, Product $product)
{
$this->product = $product;
$this->user = $user;
}
public function via($notifiable)
{
return ['database', 'broadcast'];
}
public function toDatabase($notifiable)
{
return [
'user_id' => $this->user->id,
'user_username' => $this->user->username,
'product_id' => $this->product->id,
'product_name' => $this->product->name,
];
}
public function toArray($notifiable)
{
return [
'id' => $this->id,
'read_at' => null,
'data' => [
'user_id' => $this->user->id,
'user_username' => $this->user->username,
'product_id' => $this->product->id,
'product_name' => $this->product->name,
],
];
}
}
Observer
public function created(Product $product)
{
$user = Auth::user();
event(new MessagePushed($user, $product));
}
Questions
How can I return live notifications as soon as event is fired in whole app? currently as my code is placed in add.vue component admins get notify IF they are in same page only :/
How do I get notifications of multiple event? let say I have another event, listener, observer for other page actions I want admin be notified of both product event and other event in whole app.
thanks
We differentiate channel types like public and private.
Public: Illuminate\Broadcasting\Channel. It works without any authentication, so anonymus users can listen to this broadcast. Like football match scores available for everyone.
Private: Illuminate\Broadcasting\PrivateChannel. It works with an authentication, so only authenticated users can listen to this broadcast. Like admins watching what specific users do or user watching their orders status.
On the otherhand there is another private channel which is Illuminate\Broadcasting\PresenceChannel. Also require authentication. In example would be much like an any chat room. Ex.: only authenticated users can communicate with each other.
So these types will define your method. And you have to follow different paths. I am very suggesting to read the Laravel documentation (https://laravel.com/docs/broadcasting), it will guide you very carefully.
In the routes/channels.php you have to decide or verify if the authenticated user can listen on this particular channel. So you have to return boolean value (true or false). So here, you are not returning any value to the client side.
Every public property in your MessagePushed broadcast event will be serialized and sent to the client side. You have to give every informational data to the MessagePushed constructor and set to property/properties. Now you can handle the received data by like that, ex:
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
auth: {
headers: {
'X-Auth-Token': "<token>"
}
}
});
window.Echo.private('userInAddProduct')
.listen('MessagePushed ', (e) => {
console.log(e.auction);
});
I recommend to create custom authentication with token usage. Above I sent token to the server to authenticate user. https://laravel.com/docs/authentication#adding-custom-guards
In your situation I would separate these two mechanism.
check the user for going to the App Product page
notify the admin
For the 2. point would be enough to use the Illuminate\Broadcasting\PrivateChannel. So there is need for websocket on the admin page.
The 1. point could be tricky, because it's depends on your requirments. It is enough to notify the admin about user goes to exact App Product page?
If yes, then you will trigger this broadcast event in your controller when user goes to that page.
If not enough, then you need another websocket on the user page to "poll" your server and send tick broadcast to the admin in every X seconds.
Trigger broadcast event:
broadcast(new App\Events\MessagePushed($user));
My taste of usage is:
client side: socket.io-client
sever side: laravel-echo-server
redis broadcaster
custom authentication mechanism
Hope, I could help!
Solved
Here is how I done it based on my need and my question (it might be somehow different if your case is different than mine)
In component that activity happens (in my case Add.vue)
Echo.join('userInAddProduct')
.here((users) => {
console.log('here: ', users);
})
.joining((user) => {
console.log('joining: ', user);
})
.leaving((user) => {
console.log('leaving: ', user);
})
.whisper('typing', {data: notf}) // this part sends my data
.notification((notification) => {
console.log('noitication listener1: ', notification.type);
});
As I needed my notification be visible in all pages to admins I've added my socket emit (i.e. Echo.listenForWhisper) into my navbar component.
mounted() {
this.notifs();
window.Echo.join('userInAddProduct')
// listen to broadcast from Add.vue
.listenForWhisper('typing', (product) => {
// add it into my navbar notifications list
this.notifications.unshift(product);
});
}
Now each time user adds new product to server admin gets notified.
note To see my other settings for this function to happen please scroll back to my update part and see Event, Listener & Observe files code. (you make those files and all this codes above to your preferred components then you'll have real time notifications).
I recently wrote an article How to use Laravel WebSockets for NuxtJs notifications where I described in sufficient detail including the event setting for Laravel WebSockets. I hope you find it useful.

Laravel Broadcasting, how to auth to a presence channel?

I'm trying to join a presence channel (Public channels work well), but I can't get this to work:
Vue code:
mounted(){
Echo.join('game.' + "0").here((users) => {
alert("In the channel!");
})
.joining((user) => {
console.log("Someone entered");
})
.leaving((user) => {
console.log(user.name);
})
.listen('GameEvent', (e) => {
console.log("Hey")
});
Echo.channel('NewSentence')
.listen('NewSentence',(sentence)=>{
alert("HOLA");
});
}
I'm trying to join the channel "game.0". As I'm using Laravel Passport I need to authenticate myself with a token, and that is working. Sending the auth request for Laravel Echo returns a key, but the JavaScript events are not triggering .here(), .listening() ....
BroadcastService provider boot function:
public function boot() {
Broadcast::routes(["middleware" => "auth:api"]);
require base_path('routes/channels.php');
}
channels.php
Broadcast::channel('game.0', function ($user,$id) {
return ['id' => $user->id];
});
The auth route:
Route::post('/broadcasting/auth', function(Request $request){
$pusher = new Pusher\Pusher(
env('PUSHER_APP_KEY'),
env('PUSHER_APP_SECRET'),
env('PUSHER_APP_ID'),
array(
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => false,
'host' => '127.0.0.1',
'port' => 6001,
'scheme' => 'http',
)
);
return $pusher->socket_auth($request->request->get('channel_name'),$request->request->get('socket_id'));
});
Do I need to do something extra to make it work? This is the auth request:
EDIT:
GameEvent event:
class GameEvent implements ShouldBroadcastNow {
use Dispatchable, InteractsWithSockets, SerializesModels;
public $gameEvent;
public $gameId;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($gameEvent, $gameId) {
//
$this->gameEvent = $gameEvent;
$this->gameId = $gameId;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\PresenceChannel|array
*/
public function broadcastOn() {
//return new PrivateChannel('channel-name');
return new PresenceChannel('game.0');
}
public function broadcastWith() {
return $this->gameEvent;
}
}
EDIT:
I've hardcoded the names: 'game.0' is now hardcoded in the routes/channels.php route, in the Echo connection and in the GameEvent. I also removed broadcastAs(). After entering the laravel-websockets debugging dashboard I found that the channel I want to subscribe doesn't even appear. It looks like it won't start a connection, but I can't figure out what it going on.
I hardcoded the
The problem here seems to be that the Echo is not listening on proper channel. First of all the Echo.join is using channel game.0 in which 0 is a user's id, and i don't think that there is actually a user with id 0. Secondly, you are broadcasting as
GameEvent
and Echo is connecting to channel named game.{id} I suggest that you either remove the broadcastAs() function from your event file or listen on GameEvent. Also use the websocket dashboard for testing this. The dashboard will be available at
/laravel-websockets
route automatically, which is available only for local environment so make sure that environment is local in your .env.
Use the debugging dashboard provided by laravel-websockets to send data to channels, first connect to your web socket within the dashboard then just enter the channel name, event name and data in JSON format and hit send on the dashboard.
Try finding out if that helps with resolving your problem.
I also recommend thoroughly reading laravel's official documentation on broadcasting as well as laravel-websockets debugging dashboard guide.
Also update what you got in result to this question.

Facebook login in laravel 5.2 can't hold the session after redirect

I am using Facebook PHP SDK to log my user.
I created a guard called login for this
Here is my config file of auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin'=>[
'driver'=>'session',
'provider'=>'adminusers',
],
'verify'=>[
'driver'=>'session',
'provider'=>'verify',
],
'login'=>[
'driver'=>'session',
'provider'=>'users'
]
],
to access Facebook api i created a class in App\services namespace called it Facebook
App\Services\Facbook.php
<?php
namespace App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use App\Extensions\Facebook\FacebookLaravelPersistentDataHandler;
use Facebook\Facebook as FB;
use App;
class Facebook{
protected $fb;
protected $helper;
protected $permission;
protected $log;
protected $canvashelper;
protected $persistentDataHandler;
function __construct()
{
$this->fb = new FB([
'app_id'=>Config::get('facebook.app_id'),
'app_secret'=>Config::get('facebook.app_secret'),
'default_graph_version' => Config::get('facebook.default_graph_version'),
'persistent_data_handler' => new FacebookLaravelPersistentDataHandler(),
]);
$this->helper = $this->fb->getRedirectLoginHelper();
$this->permission = Config::get('facebook.permission');
$this->log = new Logging(Config::get('facebook.logfile'),'Facebook Log');
$this->canvashelper = $this->fb->getCanvasHelper();
$this->persistentDataHandler = new FacebookLaravelPersistentDataHandler();
}
public function FBAuthUrl()
{
if($this->isFBAuth())
{
return $this->helper->getLogoutUrl($this->persistentDataHandler->get('facebook_access_token'),route('facebook.logout'));
}
else
{
return $this->helper->getLoginUrl(route('facebook.callback'),$this->permission);
}
}
public function LoginCallback()
{
$accessToken = $this->helper->getAccessToken();
if(isset($accessToken))
{
$this->persistentDataHandler->set('facebook_access_token',(string) $accessToken);
}
}
public function isFBAuth()
{
return $this->persistentDataHandler->has('facebook_access_token');
}
public function getFBUser()
{
if($this->isFBAuth())
{
$this->fb->setDefaultAccessToken($this->persistentDataHandler->get('facebook_access_token'));
/*,user_birthday,user_tagged_places*/
$response = $this->fb->get("/me?fields=id,name,first_name,last_name,age_range,link,gender,locale,picture,timezone,updated_time,verified,email");
return $response->getGraphUser();
}
else
{
return false;
}
}
public function logout()
{
$this->persistentDataHandler->delete('facebook_access_token');
$this->persistentDataHandler->delete('state');
}
}
And Here is my UserController Where i write my login logic
class UserController extends Controller
{
.....
/*
* Facebook login callback function
* #param Object App\services\Facebook
* return redirect
*/
public function fbLogin(Facebook $facebook)
{
$facebook->LoginCallback();
/*
* get the usergraphnode from facebook
*/
$fbUser = $facebook->getFBUser();
/*
* Convert UserGraphNode User To Eloquent User
*/
$user = $this->getFBLoggedUser($fbUser);
/*
* Here Log the user in laravel System
*/
Auth::guard('login')->login($user);
//dump(Auth::guard($this->guard)->user());
dump(session()->all());
return reidrect('/');
}
public function getFBLoggedUser($fbUser)
{
if(User::where('email','=',$fbUser->getField('email'))->count())
{
$user = User::where('email','=',$fbUser->getField('email'))->first();
if($user->fb_app_id){
$user->fb_app_id = $fbUser->getField('id');
$user->save();
}
}
else
{
$user = $this->FBregister($fbUser);
}
return $user;
}
/**
* Register The user logged in from Facebook
*
* #param \Facebook\GraphNodes\GraphUser;
*
* return \App\Models\User
*/
public function FBregister($fbUser)
{
$user = new User();
$user->fname = $fbUser->getField('first_name');
$user->lname = $fbUser->getField('last_name');
$user->gender = $fbUser->getField('gender');
$user->email = $fbUser->getField('email');
$user->fb_app_id = $fbUser->getField('id');
$picture = $fbUser->getField('picture');
if($picture->isSilhouette()){
$user->profile_image = $picture->getUrl();
}
$user->save();
return $user;
}
.........
}
On Successful Facebook login redirect i am calling UserController#fbLogin
after calling Auth::guard()->login() i dump session it successfully show a login_login_randomstring=>UserId i session . but When i redirect it all session data lost.
But the weird thing is that it only happen when it calling through facebook redirect. If i use this function like normal login routes it works perfactaly like this
in route.php
Route::get('/login','UserController#login');
and in UserController
function login(){
$user = User::find(12);
Auth::guard('login')->login($user);
return redirect('/');
}
Using this method i can easily access Session data after redirecting from here but in facebook case it doesn't happening.
I stuck here for two days please anyone can help me
[Note: Please don't mention in your answer that i should grouped my routes in web middleware. ]
After digging very deep in laravel i finally found what i was doing wrong. And i am posting may be it help some in future.
Important thing :- Laravel save session very last in its request life-cycle. It saves session it sends header response. So if we echo something in controller class then it will send header response without doing saving session and our session will not save. In my case i use dump function in my controller which terminate the laravel default life-cycle and forcefully send header response to browser. that's why all of session data is lost. i remove dump() form my code and everything start working correctly
According to API documentation https://laravel.com/api/5.2/Illuminate/Auth/Guard.html you should call user() method to get the currently authenticated user. So i would suggest that instead of Auth::guard() use Auth::user($user).
try to use plugin socialite for login with facebook socialite
Facebook php sdk use $_SESSION.In laravel you cannot access this variable,laravel use personal class for session.
According to api code and your facebook documentation. Simple session working with request. You can save your data with
For put session in value
Session::put('userid','1');
Retrieve the value
$request->session()->get('userid') //or
{!! Session::get('userid') !!}
Very useful thing in your case.

Categories