Laravel 11 Workgroup: Creating Collaborative Environments (Linked By Email)

Mohamad's interest is in Programming (Mobile, Web, Database and Machine Learning). He is studying at the Center For Artificial Intelligence Technology (CAIT), Universiti Kebangsaan Malaysia (UKM).
that caters for different project requirements, communication styles, and workflow preferences.

[0] Create Laravel project with Auth API
Follow the previous article, or download a quick start project.
[1] Create migration
Run Artisan command:
php artisan make:migration create_workgroups_table
Edit the migration file.
(file → database\migrations\yyyy_mm_dd_hhmmss_create_workgroups_table.php)
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWorkgroupsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('workgroups', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('refr');
$table->text('desc');
$table->string('locn');
$table->integer('estartdate');
$table->string('key1');
$table->string('key2');
$table->string('key3');
$table->string('key4');
$table->string('key5');
$table->integer('n');
$table->integer('admn');
$table->integer('cord');
$table->integer('oper');
$table->integer('mngr');
$table->string('mngr_email');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('workgroups');
}
}
Create Workgroup model.
php artisan make:model Workgroup
Edit Workgroup model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Workgroup extends Model
{
use HasFactory;
protected $fillable = [
'name',
'refr',
'desc',
'locn',
'estartdate',
'key1',
'key2',
'key3',
'key4',
'key5',
'n',
'admn',
'cord',
'oper',
'mngr',
'mngr_email',
];
}
Run Artisan command to perform migration.
php artisan migrate
[2] Create Web Controller
Create controller script file
php artisan make:controller WorkgroupsController
Edit controller script file.
(file → app\Http\Controllers\WorkgroupsController.php)
<?php
namespace App\Http\Controllers;
use App\Models\Workgroup;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class WorkgroupsController extends Controller
{
public function index()
{
$user = Auth::user();
$workgroups = Workgroup::where('mngr_email', $user->email)
->get();
return view('workgroups.index', [
'workgroups' => $workgroups,
]);
}
public function store(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|string|max:255',
'refr' => 'required|string',
'desc' => 'required|string',
'locn' => 'required|string',
'estartdate' => 'required|integer',
'key1' => 'required|string',
'key2' => 'required|string',
'key3' => 'required|string',
'key4' => 'required|string',
'key5' => 'required|string',
'n' => 'required|integer',
]);
$user = Auth::user();
$workgroup = Workgroup::create([
'mngr_email' => $user->email,
'name' => $validatedData['name'],
'refr' => $validatedData['refr'],
'desc' => $validatedData['desc'],
'locn' => $validatedData['locn'],
'estartdate' => $validatedData['estartdate'],
'key1' => $validatedData['key1'],
'key2' => $validatedData['key2'],
'key3' => $validatedData['key3'],
'key4' => $validatedData['key4'],
'key5' => $validatedData['key5'],
'admn' => 0,
'cord' => 0,
'oper' => 0,
'mngr' => $user->id,
'n' => $validatedData['n'],
]);
return redirect()->route('workgroups.index')
->with('success', 'Workgroup created successfully.');
}
}
[3] Create Web View
This step assumes that you have integrated Breeze into your system. Refer to Blade Templates guide for more information about the package.
Edit workgroups.index:
(file → resources\views\workgroups\index.blade.php)
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Your Workgroups') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<script>
document.addEventListener('DOMContentLoaded', function() {
const buttonShowFormNewWorkgroup = document.getElementById('buttonShowFormNewWorkgroup');
const formNewWorkgroup = document.getElementById('newWorkgroupForm1');
const buttonCancelFormNewWorkgroup = document.getElementById('buttonCancelFormNewWorkgroup');
buttonShowFormNewWorkgroup.addEventListener('click', function() {
formNewWorkgroup.classList.toggle('hidden');
buttonShowFormNewWorkgroup.classList.add('hidden');
});
buttonCancelFormNewWorkgroup.addEventListener('click', function() {
formNewWorkgroup.classList.toggle('hidden');
buttonShowFormNewWorkgroup.classList.remove('hidden');
});
});
</script>
@if (session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative" role="alert">
<strong class="font-bold">Success!</strong>
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
<br/>
<x-primary-button id="buttonShowFormNewWorkgroup">
New Workgroup
</x-primary-button>
<div id="newWorkgroupForm1" class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg hidden">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('New Workgroup') }}
</h2>
<form action="{{ route('workgroups.store') }}" method="POST">
@csrf
<div class="mb-4">
<label for="name" class="block text-gray-700 font-semibold mb-2">Name</label>
<input type="text" id="name" name="name" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="refr" class="block text-gray-700 font-semibold mb-2">Reference</label>
<input type="text" id="refr" name="refr" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="desc" class="block text-gray-700 font-semibold mb-2">Description</label>
<textarea id="desc" name="desc" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">-</textarea>
</div>
<div class="mb-4">
<label for="locn" class="block text-gray-700 font-semibold mb-2">Location</label>
<input type="text" id="locn" name="locn" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="estartdate" class="block text-gray-700 font-semibold mb-2">Start Date</label>
<input type="number" id="estartdate" value="0" name="estartdate" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="key1" class="block text-gray-700 font-semibold mb-2">Key 1</label>
<input type="text" id="key1" name="key1" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="key2" class="block text-gray-700 font-semibold mb-2">Key 2</label>
<input type="text" id="key2" name="key2" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="key3" class="block text-gray-700 font-semibold mb-2">Key 3</label>
<input type="text" id="key3" name="key3" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="key4" class="block text-gray-700 font-semibold mb-2">Key 4</label>
<input type="text" id="key4" name="key4" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="key5" class="block text-gray-700 font-semibold mb-2">Key 5</label>
<input type="text" id="key5" name="key5" value="-" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<div class="mb-4">
<label for="n" class="block text-gray-700 font-semibold mb-2">N</label>
<input type="number" id="n" name="n" value="0" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring focus:border-blue-300">
</div>
<x-secondary-button id="buttonCancelFormNewWorkgroup">
Cancel
</x-secondary-button>
<x-primary-button id="buttonSaveFormNewWorkgroup" type="submit">
Save Workgroup
</x-primary-button>
</form>
</div>
@if ($workgroups->count() > 0)
<div class="mt-4 space-y-4">
@foreach ($workgroups as $workgroup)
<div class="bg-white shadow-md rounded-lg p-4 border border-gray-300 border-solid border-1">
<div class="flex items-center justify-between">
<div>
<h5 class="font-semibold">{{ $workgroup->name }}</h5>
<p class="text-gray-600">{{ $workgroup->desc }}</p>
<p class="text-gray-600">Reference: {{ $workgroup->refr }}</p>
<p class="text-gray-600">Location: {{ $workgroup->locn }}</p>
<p class="text-gray-600">Start Date: {{ $workgroup->estartdate }}</p>
<p class="text-gray-600">Key 1: {{ $workgroup->key1 }}</p>
<p class="text-gray-600">Key 2: {{ $workgroup->key2 }}</p>
<p class="text-gray-600">Key 3: {{ $workgroup->key3 }}</p>
<p class="text-gray-600">Key 4: {{ $workgroup->key4 }}</p>
<p class="text-gray-600">Key 5: {{ $workgroup->key5 }}</p>
<p class="text-gray-600">N: {{ $workgroup->n }}</p>
</div>
<div class="space-x-2">
<a href="#" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Edit
</a>
<a href="#" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
Delete
</a>
</div>
</div>
</div>
@endforeach
</div>
@else
<p class="mt-4 text-gray-600">You haven't created any workgroups yet.</p>
@endif
</div>
</div>
</div>
</div>
</x-app-layout>
Add the navigation link for the workgroups page in the navigation layout file:
(file →resources\views\layouts\navigation.blade.php)
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
<x-nav-link :href="route('workgroups.index')" :active="request()->routeIs('workgroups.*')">
{{ __('Workgroups') }}
</x-nav-link>
</div>
[4] Update Web Route
...
use App\Http\Controllers\WorkgroupsController;
Route::get('/workgroups', [WorkgroupsController::class, 'index'])->name('workgroups.index');
Route::get('/workgroups/create', [WorkgroupsController::class, 'create'])->name('workgroups.create');
Route::post('/workgroups', [WorkgroupsController::class, 'store'])->name('workgroups.store');
...
[5] Test
Click New Workgroup button.

Enter details of the Workgroup e.g.:
Name = Group1
Description=This is Group1
Click Save Workgroup button.

Outcome:

Download:
https://archive.org/download/laravelprojects/lara11breeze_userapi_workgroups_20240409.zip
Further reading:
If you want to implement belongs_to and has_many relationship, refer https://hashnotes.hashnode.dev/laravel-11-workgroup-creating-collaborative-environments