
In this part we will begin the first steps of creating the CRM modules and we will start by creating the users module CRUD.
Series Topics:
- Part 1: Preparation
- Part 2: Database
- Part 3: Models & Relations
- Part 4: Preparing Login Page
- Part 5: Users Module
- Part 6: Roles & Permissions
- Part 7: Documents Module
- Part 8: Contacts Module
- Part 9: Tasks Module
- Part 10: Mailbox Module
- Part 11: Mailbox Module Complete
- Part 12: Calendar Module
- Part 13: Finishing
CRM systems usually contain long forms with many many fields, so to manipulate those forms using the classic ways of creating the forms manually is a tedious task.
So to make the development easy i will use a CRUD generator to generate form fields easily and then i will modify those fields later, for the purpose of this tutorial i will use a widely used package which is appzcoder/crud-generator.
Â
Installation
composer require appzcoder/crud-generator --dev
After that, publish its assets using the vendor:publish
Artisan command:
php artisan vendor:publish --provider="Appzcoder\CrudGenerator\CrudGeneratorServiceProvider"
Once executed this command the package created a configuration file in config/crudgenerator.php and some other files in resources/crud-generator. we will update those files in the next section.
Modifying CRUD Generator
The crud generator uses the default laravel layout but we need to update that to be suited with the current theme so you have to do the below modifications:
config/crudgenerator.php
..... return [ ... 'custom_template' => true, .... ];
resources/crud-generator/views/html/create.blade.stub
@extends('layout.app') @section('title', ' | Create %%modelName%%') @section('content') <section class="content-header"> <h1> Create New %%modelName%% </h1> <ol class="breadcrumb"> <li><a href="{{ url('/%%routeGroup%%') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="{{ url('/%%routeGroup%%%%viewName%%') }}">%%crudNameCap%%</li> <li class="active">Create</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <a href="{{ url('/%%routeGroup%%%%viewName%%') }}" title="Back"><button class="btn btn-warning btn-sm"><i class="fa fa-arrow-left" aria-hidden="true"></i> Back</button></a> <br /> <br /> @if ($errors->any()) <ul class="alert alert-danger"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="{{ url('/%%routeGroup%%%%viewName%%') }}" accept-charset="UTF-8" enctype="multipart/form-data"> {{ csrf_field() }} @include ('%%viewTemplateDir%%.form', ['formMode' => 'create']) </form> </div> </div> </div> </div> </section> @endsection
resources/crud-generator/views/html/edit.blade.stub
@extends('layout.app') @section('title', ' | Edit %%modelName%%') @section('content') <section class="content-header"> <h1> Edit %%modelName%% #{{ $%%crudNameSingular%%->%%primaryKey%% }} </h1> <ol class="breadcrumb"> <li><a href="{{ url('/%%routeGroup%%') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="{{ url('/%%routeGroup%%%%viewName%%') }}">%%crudNameCap%%</li> <li class="active">Edit</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <a href="{{ url('/%%routeGroup%%%%viewName%%') }}" title="Back"><button class="btn btn-warning btn-sm"><i class="fa fa-arrow-left" aria-hidden="true"></i> Back</button></a> <br /> <br /> @if ($errors->any()) <ul class="alert alert-danger"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="{{ url('/%%routeGroup%%%%viewName%%/' . $%%crudNameSingular%%->%%primaryKey%%) }}" accept-charset="UTF-8" enctype="multipart/form-data"> {{ method_field('PATCH') }} {{ csrf_field() }} @include ('%%viewTemplateDir%%.form', ['formMode' => 'edit']) </form> </div> </div> </div> </div> </section> @endsection
resources/crud-generator/views/html/index.blade.stub
@extends('layout.app') @section('title', ' | List %%crudNameCap%%') @section('content') <section class="content-header"> <h1> %%crudNameCap%% </h1> <ol class="breadcrumb"> <li><a href="{{ url('/admin') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="active">%%crudNameCap%%</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <a href="{{ url('/%%routeGroup%%%%viewName%%/create') }}" class="btn btn-success btn-sm pull-right" title="Add New %%modelName%%"> <i class="fa fa-plus" aria-hidden="true"></i> Add New </a> <form method="GET" action="{{ url('/%%routeGroup%%%%viewName%%') }}" accept-charset="UTF-8" class="form-inline my-2 my-lg-0" role="search"> <div class="input-group"> <input type="text" class="form-control" name="search" placeholder="Search..." value="{{ request('search') }}"> <span class="input-group-btn"> <button class="btn btn-secondary" type="submit"> <i class="fa fa-search"></i> </button> </span> </div> </form> <br/> <br/> <div class="table-responsive"> <table class="table"> <thead> <tr> <th>#</th>%%formHeadingHtml%%<th>Actions</th> </tr> </thead> <tbody> @foreach($%%crudName%% as $item) <tr> <td>{{ $item->id }}</td> %%formBodyHtml%% <td> <a href="{{ url('/%%routeGroup%%%%viewName%%/' . $item->%%primaryKey%%) }}" title="View %%modelName%%"><button class="btn btn-info btn-sm"><i class="fa fa-eye" aria-hidden="true"></i> View</button></a> <a href="{{ url('/%%routeGroup%%%%viewName%%/' . $item->%%primaryKey%% . '/edit') }}" title="Edit %%modelName%%"><button class="btn btn-primary btn-sm"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</button></a> <form method="POST" action="{{ url('/%%routeGroup%%%%viewName%%' . '/' . $item->%%primaryKey%%) }}" accept-charset="UTF-8" style="display:inline"> {{ method_field('DELETE') }} {{ csrf_field() }} <button type="submit" class="btn btn-danger btn-sm" title="Delete %%modelName%%" onclick="return confirm('Confirm delete?')"><i class="fa fa-trash-o" aria-hidden="true"></i> Delete</button> </form> </td> </tr> @endforeach </tbody> </table> <div class="pagination-wrapper"> {!! $%%crudName%%->appends(['search' => Request::get('search')])->render() !!} </div> </div> </div> </div> </div> </div> </section> @endsection
resources/crud-generator/views/html/show.blade.stub
@extends('layout.app') @section('title', ' | Show %%modelName%%') @section('content') <section class="content-header"> <h1> %%modelName%% #{{ $%%crudNameSingular%%->%%primaryKey%% }} </h1> <ol class="breadcrumb"> <li><a href="{{ url('/%%routeGroup%%') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="{{ url('/%%routeGroup%%%%viewName%%') }}">%%crudNameCap%%</li> <li class="active">Show</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <a href="{{ url('/%%routeGroup%%%%viewName%%') }}" title="Back"><button class="btn btn-warning btn-sm"><i class="fa fa-arrow-left" aria-hidden="true"></i> Back</button></a> <a href="{{ url('/%%routeGroup%%%%viewName%%/' . $%%crudNameSingular%%->%%primaryKey%% . '/edit') }}" title="Edit %%modelName%%"><button class="btn btn-primary btn-sm"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</button></a> <form method="POST" action="{{ url('%%routeGroup%%%%crudName%%' . '/' . $%%crudNameSingular%%->%%primaryKey%%) }}" accept-charset="UTF-8" style="display:inline"> {{ method_field('DELETE') }} {{ csrf_field() }} <button type="submit" class="btn btn-danger btn-sm" title="Delete %%modelName%%" onclick="return confirm('Confirm delete?')"><i class="fa fa-trash-o" aria-hidden="true"></i> Delete</button> </form> <br/> <br/> <div class="table-responsive"> <table class="table"> <tbody> <tr> <th>ID</th><td>{{ $%%crudNameSingular%%->%%primaryKey%% }}</td> </tr> %%formBodyHtmlForShowView%% </tbody> </table> </div> </div> </div> </div> </div> </section> @endsection
As shown above i instructed the package to use custom template by specifying ‘custom_template’ => true in the config file next we updated the files of the crud generator template located in resources/crud-generator.
Generating CRM Modules
I will start by running the commands of create the modules views and controllers, i will start with the users and roles modules because the other modules depend on them.
Users CRUD
Run the following crud command in terminal to generate the views:
php artisan crud:view users --fields="name#string; email#string; position_title#string; phone#string; image#string; parent_id#integer" --view-path="pages" --route-group=admin --form-helper=html --validations="name#required; email#required|email|unique:users,email; password#required"
This command will create the views of (Create, Read, Update, Delete) of the users table in resources/views/pages folder.
Next create the Users controller using the crud:
php artisan crud:controller UsersController --crud-name=users --model-name=User --view-path="pages" --route-group=admin
This command create a resource controller similar to laravel native command instead it will populate it with code ready. Everything is working fine but we need to some code modifications.
Modifying users module
Make the following modifications to the below files:
app/Http/Controllers/UsersController.php
<?php namespace App\Http\Controllers; use App\Helpers\MailerFactory; use App\Http\Requests; use App\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class UsersController extends Controller { protected $mailer; public function __construct(MailerFactory $mailer) { $this->middleware('admin', ['except' => ['getProfile', 'getEditProfile', 'postEditProfile']]); $this->mailer = $mailer; } /** * Display a listing of the resource. * * @return \Illuminate\View\View */ public function index(Request $request) { $keyword = $request->get('search'); $perPage = 25; if (!empty($keyword)) { $users = User::where('name', 'like', "%$keyword%")->orWhere('email', 'like', "%$keyword%")->paginate($perPage); } else { $users = User::latest()->paginate($perPage); } return view('pages.users.index', compact('users')); } /** * Show the form for creating a new resource. * * @return \Illuminate\View\View */ public function create() { $parents = User::all(); return view('pages.users.create', compact('parents')); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function store(Request $request) { $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users,email', 'password' => 'required', 'image' => 'image|mimes:jpeg,png,jpg,gif' ]); $requestData = $request->except(['is_profile', '_token']); $requestData['password'] = bcrypt($requestData['password']); $requestData['is_active'] = isset($requestData['is_active'])?1:0; if ($request->hasFile('image')) { checkDirectory("users"); $requestData['image'] = uploadFile($request, 'image', public_path('uploads/users')); } if(($count = User::all()->count()) && $count == 0) { $requestData['is_admin'] = 1; } User::create($requestData); return redirect('admin/users')->with('flash_message', 'User added!'); } /** * Display the specified resource. * * @param int $id * * @return \Illuminate\View\View */ public function show($id) { $user = User::findOrFail($id); return view('pages.users.show', compact('user')); } /** * Show the form for editing the specified resource. * * @param int $id * * @return \Illuminate\View\View */ public function edit($id) { $user = User::findOrFail($id); return view('pages.users.edit', compact('user')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function update(Request $request, $id) { $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users,email,' . $id, 'image' => 'image|mimes:jpeg,png,jpg,gif' ]); $requestData = $request->except(['_token']); if ($request->hasFile('image')) { checkDirectory("users"); $requestData['image'] = uploadFile($request, 'image', public_path('uploads/users')); } $user = User::findOrFail($id); $old_is_active = $user->is_active; if($user->is_admin == 0) { $requestData['is_active'] = isset($requestData['is_active']) ? 1 : 0; } $user->update($requestData); // send notification email if($user->is_admin == 0 && getSetting("enable_email_notification") == 1 && $requestData['is_active'] != $old_is_active) { if($requestData['is_active'] == 1) { $subject = "Your mini crm account have been activated"; } else { $subject = "Your mini crm account have been deactivated"; } $this->mailer->sendActivateBannedEmail($subject, $user); } return redirect('admin/users')->with('flash_message', 'User updated!'); } /** * Remove the specified resource from storage. * * @param int $id * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function destroy($id) { User::destroy($id); return redirect('admin/users')->with('flash_message', 'User deleted!'); } /** * show user profile * * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function getProfile() { $user = User::findOrFail(Auth::user()->id); return view('pages.users.profile.view', compact('user')); } public function getEditProfile() { $user = User::findOrFail(Auth::user()->id); return view('pages.users.profile.edit', compact('user')); } public function postEditProfile(Request $request) { $id = Auth::user()->id; $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users,email,' . $id, 'image' => 'image|mimes:jpeg,png,jpg,gif' ]); $requestData = $request->except(['_token']); if ($request->hasFile('image')) { checkDirectory("users"); $requestData['image'] = uploadFile($request, 'image', public_path('uploads/users')); } if(!empty($requestData['password'])) { $requestData['password'] = bcrypt($requestData['password']); } else { unset($requestData['password']); } $user = User::findOrFail($id); $user->update($requestData); return redirect('admin/my-profile')->with('flash_message', 'Profile updated!'); } }
resources/views/pages/users/create.blade.php
@extends('layout.app') @section('title', ' | Create user') @section('content') <section class="content-header"> <h1> Create New user </h1> <ol class="breadcrumb"> <li><a href="{{ url('/admin/') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li><a href="{{ url('/admin/users') }}"> Users </a></li> <li class="active">Create</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <a href="{{ url('/admin/users') }}" title="Back"><button class="btn btn-warning btn-sm"><i class="fa fa-arrow-left" aria-hidden="true"></i> Back</button></a> <br /> <br /> @if ($errors->any()) <ul class="alert alert-danger"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="{{ url('/admin/users') }}" accept-charset="UTF-8" enctype="multipart/form-data"> {{ csrf_field() }} @include ('pages.users.form', ['formMode' => 'create']) </form> </div> </div> </div> </div> </section> @endsection
resources/views/pages/users/edit.blade.php
@extends('layout.app') @section('title', ' | Edit user') @section('content') <section class="content-header"> <h1> Edit user #{{ $user->id }} </h1> <ol class="breadcrumb"> <li><a href="{{ url('/admin/') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li><a href="{{ url('/admin/users') }}"> Users </a></li> <li class="active">Edit</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <a href="{{ url('/admin/users') }}" title="Back"><button class="btn btn-warning btn-sm"><i class="fa fa-arrow-left" aria-hidden="true"></i> Back</button></a> <br /> <br /> @if ($errors->any()) <ul class="alert alert-danger"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="{{ url('/admin/users/' . $user->id) }}" accept-charset="UTF-8" enctype="multipart/form-data"> {{ method_field('PATCH') }} {{ csrf_field() }} @include ('pages.users.form', ['formMode' => 'edit']) </form> </div> </div> </div> </div> </section> @endsection
resources/views/pages/users/form.blade.php
<div class="form-group {{ $errors->has('name') ? 'has-error' : ''}}"> <label for="name" class="control-label">{{ 'Name' }}</label> <input class="form-control" name="name" type="text" id="name" value="{{ isset($user->name) ? $user->name : ''}}" > {!! $errors->first('name', '<p class="help-block">:message</p>') !!} </div> <div class="form-group {{ $errors->has('email') ? 'has-error' : ''}}"> <label for="email" class="control-label">{{ 'Email' }}</label> <input class="form-control" name="email" type="text" id="email" value="{{ isset($user->email) ? $user->email : ''}}" > {!! $errors->first('email', '<p class="help-block">:message</p>') !!} </div> @if($formMode == 'create') <div class="form-group {{ $errors->has('password') ? 'has-error' : ''}}"> <label for="password" class="control-label">{{ 'Password' }}</label> <input class="form-control" name="password" type="password" id="password" value="{{ isset($user->password) ? $user->password : ''}}" > {!! $errors->first('password', '<p class="help-block">:message</p>') !!} </div> @endif <div class="form-group {{ $errors->has('position_title') ? 'has-error' : ''}}"> <label for="position_title" class="control-label">{{ 'Position Title' }}</label> <input class="form-control" name="position_title" type="text" id="position_title" value="{{ isset($user->position_title) ? $user->position_title : ''}}" > {!! $errors->first('position_title', '<p class="help-block">:message</p>') !!} </div> <div class="form-group {{ $errors->has('phone') ? 'has-error' : ''}}"> <label for="phone" class="control-label">{{ 'Phone' }}</label> <input class="form-control" name="phone" type="text" id="phone" value="{{ isset($user->phone) ? $user->phone : ''}}" > {!! $errors->first('phone', '<p class="help-block">:message</p>') !!} </div> @if(isset($user->image) && !empty($user->image)) <img src="{{ url('uploads/users/' . $user->image) }}" width="200" height="180"/> @endif <div class="form-group {{ $errors->has('image') ? 'has-error' : ''}}"> <label for="image" class="control-label">{{ 'Image' }}</label> <input class="form-control" name="image" type="file" id="image" > {!! $errors->first('image', '<p class="help-block">:message</p>') !!} </div> @if($formMode == 'create' || ($formMode == 'edit' && $user->is_admin == 0)) <div class="form-group {{ $errors->has('is_active') ? 'has-error' : ''}}"> <label for="is_active" class="control-label"> <input type="checkbox" name="is_active" id="is_active" value="1" class="minimal" {{ $formMode == 'create'?"checked":($user->is_active == 1?"checked":"") }}> {{ 'Active / Banned' }} </label> {!! $errors->first('is_active', '<p class="help-block">:message</p>') !!} </div> @endif <div class="form-group"> <input class="btn btn-primary" type="submit" value="{{ $formMode === 'edit' ? 'Update' : 'Create' }}"> </div>
resources/views/pages/users/index.blade.php
@extends('layout.app') @section('title', ' | List users') @section('content') <section class="content-header"> <h1> Users </h1> <ol class="breadcrumb"> <li><a href="{{ url('/admin') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li class="active">Users</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> @include('includes.flash_message') <a href="{{ url('/admin/users/create') }}" class="btn btn-success btn-sm pull-right" title="Add New user"> <i class="fa fa-plus" aria-hidden="true"></i> Add New </a> <form method="GET" action="{{ url('/admin/users') }}" accept-charset="UTF-8" class="form-inline my-2 my-lg-0" role="search"> <div class="input-group"> <input type="text" class="form-control" name="search" placeholder="Search..." value="{{ request('search') }}"> <span class="input-group-btn"> <button class="btn btn-secondary" type="submit"> <i class="fa fa-search"></i> </button> </span> </div> </form> <br/> <br/> <div class="table-responsive"> <table class="table"> <thead> <tr> <th>#</th> <th>Name</th> <th>Email</th> <th>Position Title</th> <th>Is Admin</th> <th>Active / Banned</th> <th>Actions</th> </tr> </thead> <tbody> @foreach($users as $item) <tr> <td>{{ $item->id }}</td> <td>{{ $item->name }}</td> <td>{{ $item->email }}</td> <td>{{ $item->position_title }}</td> <td>{!! $item->is_admin == 1? '<i class="fa fa-check"></i>':'<i class="fa fa-times"></i>' !!}</td> <td>{!! $item->is_active == 1? '<i class="fa fa-check"></i>':'<i class="fa fa-ban"></i>' !!}</td> <td> <a href="{{ url('/admin/users/' . $item->id) }}" title="View user"><button class="btn btn-info btn-sm"><i class="fa fa-eye" aria-hidden="true"></i> View</button></a> <a href="{{ url('/admin/users/' . $item->id . '/edit') }}" title="Edit user"><button class="btn btn-primary btn-sm"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</button></a> @if($item->is_admin == 0) <form method="POST" action="{{ url('/admin/users' . '/' . $item->id) }}" accept-charset="UTF-8" style="display:inline"> {{ method_field('DELETE') }} {{ csrf_field() }} <button type="submit" class="btn btn-danger btn-sm" title="Delete user" onclick="return confirm('Confirm delete?')"><i class="fa fa-trash-o" aria-hidden="true"></i> Delete</button> </form> @endif </td> </tr> @endforeach </tbody> </table> <div class="pagination-wrapper"> {!! $users->appends(['search' => Request::get('search')])->render() !!} </div> </div> </div> </div> </div> </div> </section> @endsection
resources/views/pages/users/show.blade.php
@extends('layout.app') @section('title', ' | Show user') @section('content') <section class="content-header"> <h1> user #{{ $user->id }} </h1> <ol class="breadcrumb"> <li><a href="{{ url('/admin/') }}"><i class="fa fa-dashboard"></i> Dashboard</a></li> <li><a href="{{ url('/admin/users') }}"> Users </a></li> <li class="active">Show</li> </ol> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> @include('includes.flash_message') <a href="{{ url('/admin/users') }}" title="Back"><button class="btn btn-warning btn-sm"><i class="fa fa-arrow-left" aria-hidden="true"></i> Back</button></a> <a href="{{ url('/admin/users/' . $user->id . '/edit') }}" title="Edit user"><button class="btn btn-primary btn-sm"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</button></a> @if($user->is_admin == 0) <form method="POST" action="{{ url('admin/users' . '/' . $user->id) }}" accept-charset="UTF-8" style="display:inline"> {{ method_field('DELETE') }} {{ csrf_field() }} <button type="submit" class="btn btn-danger btn-sm" title="Delete user" onclick="return confirm('Confirm delete?');"><i class="fa fa-trash-o" aria-hidden="true"></i> Delete</button> </form> @endif <br/> <br/> <div class="table-responsive"> <table class="table"> <tbody> @if(!empty($user->image)) <tr> <td> <img src="{{ url('uploads/users/' . $user->image) }}" class="pull-right" width="200" height="200" /> </td> </tr> @endif <tr> <th>ID</th><td>{{ $user->id }}</td> </tr> <tr><th> Name </th><td> {{ $user->name }} </td> </tr><tr><th> Email </th><td> {{ $user->email }} </td></tr> <tr><th> Position Title </th><td> {{ $user->position_title }} </td></tr> <tr><th> Phone </th><td> {{ $user->phone }} </td></tr> <tr><th> Is Admin </th><td> {!! $user->is_admin == 1? '<i class="fa fa-check"></i>':'<i class="fa fa-times"></i>' !!} </td></tr> <tr><th> Is Active </th><td> {!! $user->is_active == 1? '<i class="fa fa-check"></i>':'<i class="fa fa-ban"></i>' !!} </td></tr> </tbody> </table> </div> </div> </div> </div> </div> </section> @endsection
resources/views/pages/users/profile/view.blade.php
@extends('layout.app') @section('title', ' | My Profile') @section('content') <section class="content-header"> <h1> My Profile </h1> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> @include('includes.flash_message') <a href="{{ url('/admin/my-profile/edit') }}" title="Edit profile"><button class="btn btn-primary btn-sm"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> Edit</button></a> <br/> <br/> <div class="table-responsive"> <table class="table"> <tbody> @if(!empty($user->image)) <tr> <td> <img src="{{ url('uploads/users/' . $user->image) }}" class="pull-right" width="200" height="200" /> </td> </tr> @endif <tr><th> Name </th><td> {{ $user->name }} </td> </tr><tr><th> Email </th><td> {{ $user->email }} </td></tr> <tr><th> Position Title </th><td> {{ $user->position_title }} </td></tr> <tr><th> Phone </th><td> {{ $user->phone }} </td></tr> </tbody> </table> </div> </div> </div> </div> </div> </section> @endsection
resources/views/pages/users/profile/edit.blade.php
@extends('layout.app') @section('title', ' | Edit My Profile') @section('content') <section class="content-header"> <h1> Edit My profile </h1> </section> <section class="content"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> @if ($errors->any()) <ul class="alert alert-danger"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="{{ url('/admin/my-profile/edit/') }}" accept-charset="UTF-8" enctype="multipart/form-data"> {{ method_field('PATCH') }} {{ csrf_field() }} <div class="form-group {{ $errors->has('name') ? 'has-error' : ''}}"> <label for="name" class="control-label">{{ 'Name' }}</label> <input class="form-control" name="name" type="text" id="name" value="{{ isset($user->name) ? $user->name : ''}}" > {!! $errors->first('name', '<p class="help-block">:message</p>') !!} </div> <div class="form-group {{ $errors->has('email') ? 'has-error' : ''}}"> <label for="email" class="control-label">{{ 'Email' }}</label> <input class="form-control" name="email" type="text" id="email" value="{{ isset($user->email) ? $user->email : ''}}" > {!! $errors->first('email', '<p class="help-block">:message</p>') !!} </div> <div class="form-group {{ $errors->has('password') ? 'has-error' : ''}}"> <label for="password" class="control-label">{{ 'Password' }}</label> <input class="form-control" name="password" type="password" id="password" value="" > {!! $errors->first('password', '<p class="help-block">:message</p>') !!} </div> <div class="form-group {{ $errors->has('position_title') ? 'has-error' : ''}}"> <label for="position_title" class="control-label">{{ 'Position Title' }}</label> <input class="form-control" name="position_title" type="text" id="position_title" value="{{ isset($user->position_title) ? $user->position_title : ''}}" > {!! $errors->first('position_title', '<p class="help-block">:message</p>') !!} </div> <div class="form-group {{ $errors->has('phone') ? 'has-error' : ''}}"> <label for="phone" class="control-label">{{ 'Phone' }}</label> <input class="form-control" name="phone" type="text" id="phone" value="{{ isset($user->phone) ? $user->phone : ''}}" > {!! $errors->first('phone', '<p class="help-block">:message</p>') !!} </div> @if(!empty($user->image)) <img src="{{ url('uploads/users/' . $user->image) }}" width="200" height="180"/> @endif <div class="form-group {{ $errors->has('image') ? 'has-error' : ''}}"> <label for="image" class="control-label">{{ 'Image' }}</label> <input class="form-control" name="image" type="file" id="image" > {!! $errors->first('image', '<p class="help-block">:message</p>') !!} </div> <div class="form-group"> <input class="btn btn-primary" type="submit" value="Update"> </div> </form> </div> </div> </div> </div> </section> @endsection
app/Helpers/MailerFactory.php
<?php namespace App\Helpers; use Illuminate\Contracts\Mail\Mailer; class MailerFactory { protected $mailer; protected $fromAddress = ''; protected $fromName = 'Mini CRM'; /** * MailerFactory constructor. * * @param Mailer $mailer */ public function __construct(Mailer $mailer) { $this->mailer = $mailer; $this->fromAddress = getSetting("crm_email"); } /** * sendActivateBannedEmail * * * @param $subject * @param $user */ public function sendActivateBannedEmail($subject, $user) { try { $this->mailer->send("emails.activate_banned", ['user' => $user, 'subject' => $subject], function($message) use ($subject, $user) { $message->from($this->fromAddress, $this->fromName) ->to($user->email)->subject($subject); }); } catch (\Exception $ex) { die("Mailer Factory error: " . $ex->getMessage()); } } }
app/Helpers/General.php
<?php use App\Models\Setting; /** * upload file * * * @param $request * @param $name * @param string $destination * @return string */ function uploadFile($request, $name, $destination = '') { $image = $request->file($name); $name = time().'.'.$image->getClientOriginalExtension(); if($destination == '') { $destination = public_path('/uploads'); } $image->move($destination, $name); return $name; } /** * add setting key and value * * * @param $key * @param $value * @return Setting|bool */ function addSetting($key, $value) { if(Setting::where('setting_key', $key)->first() != null) return false; $setting = new Setting(); $setting->setting_key = $key; $setting->setting_value = $value; $setting->save(); return $setting; } /** * get setting value by key * * @param $key * @return mixed */ function getSetting($key) { return ($setting = Setting::where('setting_key', $key)->first()) != null ? $setting->setting_value:null; } /** * check directory exists and try to create it * * * @param $directory */ function checkDirectory($directory) { try { if (!file_exists(public_path('uploads/' . $directory))) { mkdir(public_path('uploads/' . $directory)); chmod(public_path('uploads/' . $directory), 0777); } } catch (\Exception $e) { die($e->getMessage()); } } /** * check if user has permission * * * @param $permission * @return bool */ function user_can($permission) { return \Auth::user()->is_admin == 1 || \Auth::user()->can($permission); }
resources/views/emails/activate_banned.blade.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ $subject }}</title> </head> <body> <p> Hello {{ $user->name }}, </p> @if($user->is_active == 1) <p> Your account have been activated to access the mini CRM system <a href="{{ url('admin') }}"> {{ url('admin') }} </a> </p> @else <p> Your account have been deactivated to access the mini CRM system <a href="{{ url('admin') }}"> {{ url('admin') }} </a> </p> @endif </body> </html>
composer.json
....... ....... "autoload": { .... ..... "files": [ "app/Helpers/General.php" ] .... .... } ..... .....
routes/web.php
<?php Route::group(['prefix' => 'admin', 'middleware' => 'auth'], function () { Route::get('/', function () { return view('pages.home.index'); }); Route::resource('/users', 'UsersController'); Route::get('/my-profile', 'UsersController@getProfile'); Route::get('/my-profile/edit', 'UsersController@getEditProfile'); Route::patch('/my-profile/edit', 'UsersController@postEditProfile'); Route::get('/forbidden', function () { return view('pages.forbidden.forbidden_area'); }); }); Route::get('/', function () { return redirect()->to('/admin'); }); Auth::routes();
resources/views/includes/flash_message.blade.php
@if(\Session::has('flash_message')) <div class="alert alert-success">{{ \Session::get('flash_message') }}</div> @endif @if(\Session::has('flash_error')) <div class="alert alert-danger">{{ \Session::get('flash_error') }}</div> @endif @if(\Session::has('flash_warning')) <div class="alert alert-warning">{{ \Session::get('flash_warning') }}</div> @endif
In the above code i have modified the users Controller code. At first i created a new class MailerFactory, this class contain the methods for sending emails to the user.
In that case the class contain only one method which sends email when ever the user activated or banned, we will add other methods later when create the contacts and tasks modules.
Also i added another file General inside of the /Helpers folder, this file will contain helper methods to use throughout the system. Note that i update the composer.json file to autoload that file so you need to run this command after you update composer.json
composer dump-autoload
For sending the emails i use a virtual email service which simulates the sending of emails which is the mailtrap.io you need to create an account there then create a new inbox. After that go to inbox configuration page and grap the email settings and update your .env file like this:
MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=<username> MAIL_PASSWORD=<password> MAIL_ENCRYPTION=null
You need to add your valid mailtrap username and password.
Update sidebar
Now to update the sidebar to add the new item we just created so
Open resources/views/layout/sidebar.blade.php
<!-- Left side column. contains the logo and sidebar --> <aside class="main-sidebar"> <!-- sidebar: style can be found in sidebar.less --> <section class="sidebar"> <!-- Sidebar user panel --> <div class="user-panel"> <div class="pull-left image"> @if(\Auth::user()->image != null) <img src="{{ url('uploads/users/' . \Auth::user()->image) }}" class="img-circle" alt="User Image"> @else <img src="{{ url('theme/dist/img/image_placeholder.png') }}" class="img-circle" alt="User Image"> @endif </div> <div class="pull-left info"> <p>{{ \Auth::user()->name }}</p> </div> </div> <!-- sidebar menu: : style can be found in sidebar.less --> <ul class="sidebar-menu" data-widget="tree"> <li class="header">MAIN NAVIGATION</li> <li class="{{ Request::segment(2) == ""?"active":"" }}"> <a href="{{ url('/admin') }}"> <i class="fa fa-dashboard"></i> <span>Dashboard</span> </a> </li> <li class="{{ Request::segment(2) == "users"?"active":"" }} treeview"> <a href="#"> <i class="fa fa-users"></i> <span>Users Management</span> <span class="pull-right-container"> <i class="fa fa-angle-left pull-right"></i> </span> </a> <ul class="treeview-menu"> <li class="{{ Request::segment(2) == "users"?"active":"" }}"> <a href="{{ url('/admin/users') }}"><i class="fa fa-user-o"></i> Users</a> </li> </ul> </li> </ul> </section> <!-- /.sidebar --> </aside>
Continue to Part 6: The Roles and Permissions Module>>>
ErrorCall to undefined function App\Helpers\getSetting()
http://localhost:8000/admin/users
Be sure to run
composer dump-autoload
not working after running this command
Which command?
Same error here. Composer dump-autoload not working
composer dump-autoload