User to Team associations with invitation system for the Laravel 5 Framework

Related tags

Laravel teamwork
Overview

Teamwork

This package supports Laravel 6 and above.

Latest Version Software License Build Status codecov.io SensioLabsInsight Scrutinizer Code Quality

Teamwork is the fastest and easiest method to add a User / Team association with Invites to your Laravel 6+ project.

Installation

composer require mpociot/teamwork

The Teamwork Facade will be auto discovered by Laravel automatically.

Configuration

To publish Teamwork's configuration and migration files, run the vendor:publish command.

php artisan vendor:publish --provider="Mpociot\Teamwork\TeamworkServiceProvider"

This will create a teamwork.php in your config directory. The default configuration should work just fine for you, but you can take a look at it, if you want to customize the table / model names Teamwork will use.

User relation to teams

Run the migration command, to generate all tables needed for Teamwork. If your users are stored in a different table other than users be sure to modify the published migration.

php artisan migrate

After the migration, 3 new tables will be created:

  • teams — stores team records
  • team_user — stores many-to-many relations between users and teams
  • team_invites — stores pending invites for email addresses to teams

You will also notice that a new column current_team_id has been added to your users table. This column will define the Team, the user is currently assigned to.

Models

Team

Create a Team model inside app/Team.php using the following example:

<?php namespace App;

use Mpociot\Teamwork\TeamworkTeam;

class Team extends TeamworkTeam
{
}

The Team model has two main attributes:

  • owner_id — Reference to the User model that owns this Team.
  • name — Human readable name for the Team.

The owner_id is an optional attribute and is nullable in the database.

When extending TeamworkTeam, remember to change the team_model variable in config/teamwork.php to your new model. For instance: 'team_model' => App\Team::class

User

Add the UserHasTeams trait to your existing User model:

<?php namespace App;

use Mpociot\Teamwork\Traits\UserHasTeams;

class User extends Model {

    use UserHasTeams; // Add this trait to your model
}

This will enable the relation with Team and add the following methods teams(), ownedTeams() currentTeam(), invites(), isTeamOwner(), isOwnerOfTeam($team), attachTeam($team, $pivotData = []), detachTeam($team), attachTeams($teams), detachTeams($teams), switchTeam($team) within your User model.

Don't forget to dump composer autoload

composer dump-autoload

Middleware

If you would like to use the middleware to protect to current team owner then just add the middleware provider to your app\Http\Kernel.php file.

    protected $routeMiddleware = [
        ...
        'teamowner' => \Mpociot\Teamwork\Middleware\TeamOwner::class,
        ...
    ];

Afterwards you can use the teamowner middleware in your routes file like so.

Route::get('/owner', function(){
    return "Owner of current team.";
})->middleware('auth', 'teamowner');

Now only if the authenticated user is the owner of the current team can access that route.

This middleware is aimed to protect routes where only the owner of the team can edit/create/delete that model

And you are ready to go.

Usage

Scaffolding

The easiest way to give your new Laravel project Team abilities is by using the make:teamwork command.

php artisan make:teamwork

This command will create all views, routes and controllers to make your new project team-ready.

Out of the box, the following parts will be created for you:

  • Team listing
  • Team creation / editing / deletion
  • Invite new members to teams

Imagine it as a the make:auth command for Teamwork.

To get started, take a look at the new installed /teams route in your project.

Basic concepts

Let's start by creating two different Teams.

$team    = new Team();
$team->owner_id = User::where('username', '=', 'sebastian')->first()->getKey();
$team->name = 'My awesome team';
$team->save();

$myOtherCompany = new Team();
$myOtherCompany->owner_id = User::where('username', '=', 'marcel')->first()->getKey();
$myOtherCompany->name = 'My other awesome team';
$myOtherCompany->save();

Now thanks to the UserHasTeams trait, assigning the Teams to the user is uber easy:

$user = User::where('username', '=', 'sebastian')->first();

// team attach alias
$user->attachTeam($team, $pivotData); // First parameter can be a Team object, array, or id

// or eloquent's original technique
$user->teams()->attach($team->id); // id only

By using the attachTeam method, if the User has no Teams assigned, the current_team_id column will automatically be set.

Get to know my team(s)

The currently assigned Team of a user can be accessed through the currentTeam relation like this:

echo "I'm currently in team: " . Auth::user()->currentTeam->name;
echo "The team owner is: " . Auth::user()->currentTeam->owner->username;

echo "I also have these teams: ";
print_r( Auth::user()->teams );

echo "I am the owner of these teams: ";
print_r( Auth::user()->ownedTeams );

echo "My team has " . Auth::user()->currentTeam->users->count() . " users.";

The Team model has access to these methods:

  • invites() — Returns a many-to-many relation to associated invitations.
  • users() — Returns a many-to-many relation with all users associated to this team.
  • owner() — Returns a one-to-one relation with the User model that owns this team.
  • hasUser(User $user) — Helper function to determine if a user is a teammember

Team owner

If you need to check if the User is a team owner (regardless of the current team) use the isTeamOwner() method on the User model.

if( Auth::user()->isTeamOwner() )
{
    echo "I'm a team owner. Please let me pay more.";
}

Additionally if you need to check if the user is the owner of a specific team, use:

$team = Auth::user()->currentTeam;
if( Auth::user()->isOwnerOfTeam( $team ) )
{
    echo "I'm a specific team owner. Please let me pay even more.";
}

The isOwnerOfTeam method also allows an array or id as team parameter.

Switching the current team

If your Users are members of multiple teams you might want to give them access to a switch team mechanic in some way.

This means that the user has one "active" team, that is currently assigned to the user. All other teams still remain attached to the relation!

Glad we have the UserHasTeams trait.

try {
    Auth::user()->switchTeam( $team_id );
    // Or remove a team association at all
    Auth::user()->switchTeam( null );
} catch( UserNotInTeamException $e )
{
    // Given team is not allowed for the user
}

Just like the isOwnerOfTeam method, switchTeam accepts a Team object, array, id or null as a parameter.

Inviting others

The best team is of no avail if you're the only team member.

To invite other users to your teams, use the Teamwork facade.

Teamwork::inviteToTeam( $email, $team, function( $invite )
{
    // Send email to user / let them know that they got invited
});

You can also send invites by providing an object with an email property like:

$user = Auth::user();

Teamwork::inviteToTeam( $user , $team, function( $invite )
{
    // Send email to user / let them know that they got invited
});

This method will create a TeamInvite model and return it in the callable third parameter.

This model has these attributes:

  • email — The email that was invited.
  • accept_token — Unique token used to accept the invite.
  • deny_token — Unique token used to deny the invite.

In addition to these attributes, the model has these relations:

  • user() — one-to-one relation using the email as a unique identifier on the User model.
  • team() — one-to-one relation return the Team, that invite was aiming for.
  • inviter() — one-to-one relation return the User, that created the invite.

Note: The inviteToTeam method will not check if the given email already has a pending invite. To check for pending invites use the hasPendingInvite method on the Teamwork facade.

Example usage:

if( !Teamwork::hasPendingInvite( $request->email, $request->team) )
{
    Teamwork::inviteToTeam( $request->email, $request->team, function( $invite )
    {
                // Send email to user
    });
} else {
    // Return error - user already invited
}

Accepting invites

Once you invited other users to join your team, in order to accept the invitation use the Teamwork facade once again.

$invite = Teamwork::getInviteFromAcceptToken( $request->token ); // Returns a TeamworkInvite model or null

if( $invite ) // valid token found
{
    Teamwork::acceptInvite( $invite );
}

The acceptInvite method does two thing:

  • Call attachTeam with the invite-team on the currently authenticated user.
  • Delete the invitation afterwards.

Denying invites

Just like accepting invites:

$invite = Teamwork::getInviteFromDenyToken( $request->token ); // Returns a TeamworkInvite model or null

if( $invite ) // valid token found
{
    Teamwork::denyInvite( $invite );
}

The denyInvite method is only responsible for deleting the invitation from the database.

Attaching/Detaching/Invite Events

If you need to run additional processes after attaching/detaching a team from a user or inviting a user, you can Listen for these events:

\Mpociot\Teamwork\Events\UserJoinedTeam

\Mpociot\Teamwork\Events\UserLeftTeam

\Mpociot\Teamwork\Events\UserInvitedToTeam

In your EventServiceProvider add your listener(s):

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    ...
    \Mpociot\Teamwork\Events\UserJoinedTeam::class => [
        App\Listeners\YourJoinedTeamListener::class,
    ],
    \Mpociot\Teamwork\Events\UserLeftTeam::class => [
        App\Listeners\YourLeftTeamListener::class,
    ],
    \Mpociot\Teamwork\Events\UserInvitedToTeam::class => [
        App\Listeners\YourUserInvitedToTeamListener::class,
    ],
];

The UserJoinedTeam and UserLeftTeam event exposes the User and Team's ID. In your listener, you can access them like so:

<?php

namespace App\Listeners;

use Mpociot\Teamwork\Events\UserJoinedTeam;

class YourJoinedTeamListener
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  UserJoinedTeam  $event
     * @return void
     */
    public function handle(UserJoinedTeam $event)
    {
        // $user = $event->getUser();
        // $teamId = $event->getTeamId();

        // Do something with the user and team ID.
    }
}

The UserInvitedToTeam event contains an invite object which could be accessed like this:

<?php

namespace App\Listeners;

use Mpociot\Teamwork\Events\UserInvitedToTeam;

class YourUserInvitedToTeamListener
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  UserInvitedToTeam  $event
     * @return void
     */
    public function handle(UserInvitedToTeam $event)
    {
        // $user = $event->getInvite()->user;
        // $teamId = $event->getTeamId();

        // Do something with the user and team ID.
    }
}

Limit Models to current Team

If your models are somehow limited to the current team you will find yourself writing this query over and over again: Model::where('team_id', auth()->user()->currentTeam->id)->get();.

To automate this process, you can let your models use the UsedByTeams trait. This trait will automatically append the current team id of the authenticated user to all queries and will also add it to a field called team_id when saving the models.

Note:

This assumes that the model has a field called team_id

Usage

use Mpociot\Teamwork\Traits\UsedByTeams;

class Task extends Model
{
    use UsedByTeams;
}

When using this trait, all queries will append WHERE team_id=CURRENT_TEAM_ID. If theres a place in your app, where you really want to retrieve all models, no matter what team they belong to, you can use the allTeams scope.

Example:

// gets all tasks for the currently active team of the authenticated user
Task::all();

// gets all tasks from all teams globally
Task::allTeams()->get();

License

Teamwork is free software distributed under the terms of the MIT license.

'Marvel Avengers' image licensed under Creative Commons 2.0 - Photo from W_Minshull

Comments
  • Missing argument 1 for Mpociot\Teamwork\Teamwork::__construct(), called in C:\wamp\www\LOLPLATFORM\app\Http\Controllers\TeamsController.php on line 30 and defined

    Missing argument 1 for Mpociot\Teamwork\Teamwork::__construct(), called in C:\wamp\www\LOLPLATFORM\app\Http\Controllers\TeamsController.php on line 30 and defined

    i did exactly as the documentation said and i got this error when i tried to execute it -_-

    Here's what i wrote

    public function create(Request $request) { $team = new Team(); $team->owner_id = User::find($request->get('leader')); $team->name = $request->get('name'); $team->save();

    }
    
    opened by minedun6 10
  • Migration errors

    Migration errors

    I know this seems to have been addressed in issue #35 and corrected in the package code, however after installing via composer (composer require mpociot/teamwork:^5.3), publishing and attempting to run migration in my Laravel 5.5 project, I get the following errors:

    php artisan migrate
    
    In Connection.php line 664:
    
      SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter table `team_user` add constraint `team_user_user_id_foreign` foreign key (`user_id`) references `us
      ers` (`id`) on delete cascade on update cascade)
    
    
    In PDOStatement.php line 119:
    
      SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint
    
    
    In PDOStatement.php line 117:
    
      SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint
    

    After searching similar issues, I manually backed out the database changes made by the failed migration, changed increments to bigIncrements, but this had no effect - same error. I wasn't sure at which v.5 of Laravel that requirement kicked in, but figured it worth a shot.

    Has anyone else come across this error and can recommend a solution?

    opened by hondaman900 8
  • can not install for laravel 6.*

    can not install for laravel 6.*

    I am not able to install the package for laravel 6.0, however, the doc says it's for the laravel 7 and above only but I saw a commit which says added support for laravel 6.0

    Problem 1

        - Can only install one of: laravel/framework[v6.18.20, 7.x-dev].
        - Can only install one of: laravel/framework[7.x-dev, v6.18.20].
        - Can only install one of: laravel/framework[7.x-dev, v6.18.20].
        - mpociot/teamwork 6.0.0 requires laravel/framework ^7.0 -> satisfiable by laravel/framework[7.x-dev].
        - Installation request for mpociot/teamwork ^6.0 -> satisfiable by mpociot/teamwork[6.0.0].
        - Installation request for laravel/framework == 6.18.20.0 -> satisfiable by laravel/framework[v6.18.20].
    
    
    opened by cypherinc 8
  • Support 5.8 fully (including for new installs)

    Support 5.8 fully (including for new installs)

    Laravel 5.8 is out, and I see a few requests for updating this library (which I very much appreciate and enjoy using) - but #107 and #108 don't fix the migration to support new users installing from scratch due to the changes on the user table.

    This pull request adds:

    • support in composer for satisfying 5.8
    • Changes create migration to bigInteger so the foreign keys don't break when installing on fresh 5.8
    opened by ow 8
  • Non-team member can invite members to team

    Non-team member can invite members to team

    If I create a account and go to the team URL (/teams/members/1) I can invite people to that team even if I'm not a member myself. So, in effect I could join myself to any team.

    I did notice #50 where it was sugessted that certain restrictions be put in place for inviting, but I thought that this just seems inaccurate for the default.

    stale 
    opened by vmitchell85 8
  • Give this a full refresh (libraries, php version, code style, tests)

    Give this a full refresh (libraries, php version, code style, tests)

    Hi,

    As I'm using this library in some of my products I detected it's not compatible with Laravel 7. After got my hands on it, I decided to give this a whole refresh.

    This is what I did:

    • Updated all libraries to the latest versions
    • Dropped Laravel version older than 7 (cause innovation ftw …💁‍♂️)
    • Restructure the tests into its own namespaces
    • Make tests compatible with sqlite

    If you want these changes I suggest to release a new major release...

    opened by okaufmann 6
  • Undefined index: teams

    Undefined index: teams

    I'm not able to use any of the views without error.

    Going to /team returns:

    ErrorException in GenericUser.php line 98:
    Undefined index: teams
    

    I have a feeling the app/Team.php file isn't completed.. Here is my app/Team.php file:

    <?php
    
    namespace App;
    
    use Mpociot\Teamwork\TeamworkTeam;
    
    class Team extends TeamworkTeam
    {
        //
    }
    
    opened by hellozach 6
  • Updated owner() method with the correct relationship (#100 continued)

    Updated owner() method with the correct relationship (#100 continued)

    Apparently the PR #100 (trying to fix #99) didn't work out. This is a new attempt :-)


    TeamworkTeamTrait::owner() method now returns a \Illuminate\Database\Eloquent\Relations\BelongsTo to be inline with current standards on relationships

    opened by oliverpool 5
  • Modified currentTeam() method and added currentTeam attribute

    Modified currentTeam() method and added currentTeam attribute

    While having issues trying to use withPivot() on currentTeam it would just error out. Turns out hasOne doesn't have that method so decided to fix the issue so you can grab data from the pivot table.

    opened by codedninja 5
  • Allow dev to apply pivot data on attaching a team.

    Allow dev to apply pivot data on attaching a team.

    I was unable to come up with a way to unit test this change because I am unsure how to test the withPivot method with Mockery (also not sure if there were any database migrations happening, or if Mockery just simulates a database).

    I could also apply the same change to the attachTeams method, but that would require everyone else change the format of the array they send to that method - which seems a bit rude.

    opened by mikemand 5
  • Save() method not found

    Save() method not found

    This is my controller

    class TeamController extends Controller {

    public function index()
    {
        $app = new Application();
        $team = new Team($app);
        $team->owner_id = Auth::user()->id;
        $team->name = 'My awesome team';
        $team->save();
    }
    

    }

    When i execute this nothing happens, because of the $items->save() method not found in class including parent "Mpociot\Teamwork\Teamwork" class.

    Please can you help me how to save new team.

    opened by senthilvelpro 5
  • Append table name to Builder to prevent conflicting `ID`s in SQL query

    Append table name to Builder to prevent conflicting `ID`s in SQL query

    Fixes the issue "Column 'id' in where clause is ambiguous" when using additional scopes.

    Example query that fails due to not specifying the table name for id:

    SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous (SQL: select users.*, tenants_user.team_id as pivot_team_id, tenants_user.user_id as pivot_user_id, tenants_user.created_at as pivot_created_at, tenants_user.updated_at as pivot_updated_at from users inner join tenants_user on users.id = tenants_user.user_id where tenants_user.team_id = 2 and id = 1 and users.deleted_at is null limit 1)

    opened by kyranb 5
Releases(7.0.0)
Owner
Marcel Pociot
Marcel Pociot
Per-user settings repository system for Laravel

Laraconfig Per-user settings repository system for Laravel. This package allows users to have settings that can be queried, changed and even updated,

Italo 170 Oct 26, 2022
This package helps you to add user based follow system to your model.

Laravel Follow User follow unfollow system for Laravel. Related projects: Like: overtrue/laravel-like Favorite: overtrue/laravel-favorite Subscribe: o

安正超 1k Dec 31, 2022
Login system designed by fragX to validate the user and prevent unauthorized access to confidential data.

Login_System v.0.1 Login system designed by fragX to validate the user and prevent unauthorized access to confidential data. ?? Features Sign In and S

fragX 1 Jan 28, 2022
cybercog 996 Dec 28, 2022
Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

null 9 Dec 14, 2022
Simple Video is a automated H264 encryption system built on Lumen Laravel Framework

Simple Video is a automated H264 encryption system built on Lumen Laravel Framework

Azril Nazli Alias 4 Oct 5, 2022
Laravel 2-Step Verification is a package to add 2-Step user authentication to any Laravel project easily.

Laravel 2-Step verification is a package to add 2-Step user authentication to any Laravel project easily. It is configurable and customizable. It uses notifications to send the user an email with a 4-digit verification code. Laravel 2-Step Authentication Verification for Laravel. Can be used in out the box with Laravel's authentication scaffolding or integrated into other projects.

Jeremy Kenedy 204 Dec 23, 2022
Laravel Podcast is Laravel 5.5 web app that enables you to manage RSS feeds for your favorite podcasts and listen to the episodes in a seamless UI and User Authentication.

Laravel Podcast is Laravel 5.5 web app that enables you to manage RSS feeds for your favorite podcasts and listen to the episodes in a seamless UI and

Jeremy Kenedy 35 Dec 19, 2022
List of 77 languages for Laravel Framework 4, 5, 6, 7 and 8, Laravel Jetstream , Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova and Laravel Spark.

Laravel Lang In this repository, you can find the lang files for the Laravel Framework 4/5/6/7/8, Laravel Jetstream , Laravel Fortify, Laravel Cashier

Laravel Lang 6.9k Jan 2, 2023
Simple user messaging package for Laravel

Laravel Messenger This package will allow you to add a full user messaging system into your Laravel application. Leave some feedback How are you using

Chris Gmyr 2.3k Dec 29, 2022
Laravel Authentication Log is a package Log user authentication details and send new device notifications.

Laravel Authentication Log is a package which tracks your user's authentication information such as login/logout time, IP, Browser, Location, etc. as well as sends out notifications via mail, slack, or sms for new devices and failed logins.

Anthony Rappa 540 Jan 5, 2023
Thunder is an advanced Laravel tool to track user consumption using Cashier's Metered Billing for Stripe. ⚡

⚡ Thunder Thunder is an advanced Laravel tool to track user consumption using Cashier's Metered Billing for Stripe. ⚡ ?? Supporting If you are using o

Renoki Co. 10 Nov 21, 2022
A Laravel package to help track user onboarding steps.

Onboard A Laravel package to help track user onboarding steps. Installation: Install the package via composer composer require calebporzio/onboard Reg

Caleb Porzio 440 Dec 17, 2022
A simple blog app where a user can signup , login, like a post , delete a post , edit a post. The app is built using laravel , tailwind css and postgres

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Nahom_zd 1 Mar 6, 2022
Viewi for Laravel: Build full-stack and completely reactive user interfaces with PHP.

[WIP] Laravel Viewi This is just a proof-of-concept. Don't use it in production! Viewi for Laravel: Build full-stack and completely reactive user inte

Protone Media 5 Jan 26, 2022
Log user authentication actions in Laravel.

Laravel Auth Log The laravel-auth-log package will log all the default Laravel authentication events (Login, Attempting, Lockout, etc.) to your databa

Label84 29 Dec 8, 2022
Customize Login and Register Page for User/Admin in Laravel v8

Customize Login and Register Page for User/Admin in Laravel v8

Gaurang Kumar 1 Jan 28, 2022
A Multi User Chat Application With Laravel and Livewire

A Multi User Chat Application With Laravel and Livewire. where you can chat with multiple frinds at the same time. i build this with php Laravel and Livewire.

Tauseed 15 Oct 22, 2022
Laravel Belongs To User Package

If you are creating your own trait to have "belongsTo" User relationship in every model needed, you can also use this package not to define the trait on your own.

Umut Işık 5 Jul 7, 2022