Working on a Laravel project and need sample user data for testing purposes? Setting up a mock controller with some realistic user data can be a great approach, especially if you're prototyping or want to focus on front-end features without setting up a database. In this article, we’ll walk through creating a UsersController
in Laravel that provides mock user data and implements CRUD operations for demonstration and testing.
1. Setting Up the Controller
To begin, create a new controller in Laravel named UsersController
. This will serve as our mock API endpoint for managing user data. We’ll store our user data in an array, simulating a database table.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; class UsersController extends Controller { public $users; function __construct() { // Sample users data $this->users = [ [ 'id' => 1, 'name' => 'Emma Smith', 'email' => 'smith@kpmg.com', 'position' => 'Art Director', 'role' => 'Administrator', 'last_login' => 'Yesterday', 'two_steps' => false, 'joined_day' => "10 Nov 2022, 9:23 pm", 'online' => false ], // Additional sample users here ]; } }
This constructor method initializes an array of user data. Each user has attributes like id
, name
, email
, role
, and last_login
. You can customize these attributes to fit your needs.
2. Fetching Users List with Pagination and Filters
Our next step is to create a method to fetch all users, including pagination and filtering functionality. This helps us simulate a real database where we can retrieve, sort, and filter data.
public function getUsers(Request $request) { $usersCollection = collect($this->users); $page = $request->input('page', 1); $perPage = $request->input("items_per_page", 10); // Search and filter logic here // Paginate users $paginatedUsers = $this->paginate($usersCollection, $perPage, $page); return response(["data" => $paginatedUsers->toArray()], 200); }
This method retrieves users as a collection and paginates the results. We also implement a helper function paginate()
to manually paginate the data.
3. Adding a User
Adding a new user in this mock setup requires validating the input and appending it to our users array. Here’s how:
public function addUser(Request $request) { $data = json_decode($request->getContent(), true); $validator = Validator::make($data, [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255', 'role' => 'required|string|max:255', ]); if ($validator->fails()) { return response(["errors" => $validator->errors()], 422); } $data["id"] = count($this->users) + 1; $this->users[] = $data; return response(["data" => $data], 201); }
This function adds a new user only if the data is valid, simulating the behavior of a real database-backed POST
request.
4. Updating User Data
Updating a user’s information is similar to adding a new user, but here we first locate the user by ID, then update the existing array entry:
public function updateUser(Request $request, $id) { $data = json_decode($request->getContent(), true); $validator = Validator::make($data, [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255', 'role' => 'required|string|max:255', ]); if ($validator->fails()) { return response(["errors" => $validator->errors()], 422); } $user = collect($this->users)->firstWhere('id', $id); if (!$user) { return response(["message" => "User not found"], 404); } $user = array_merge($user, $data); return response(["data" => $user], 200); }
5. Deleting a User
Deleting a user is as straightforward as removing the user by ID from the array. Here’s a simple deleteUser
function:
<?php public function deleteUser($id) { $key = array_search($id, array_column($this->users, 'id')); if ($key === false) { return response(["message" => "User not found"], 404); } unset($this->users[$key]); return response(null, 204); }
6. Creating the Paginate Function
Since we’re working with an array instead of a database, we need a custom paginator. Here’s a function to handle manual pagination:
<?php public function paginate($items, $perPage = 10, $page = null) { $page = $page ?: (Paginator::resolveCurrentPage() ?: 1); $items = $items instanceof Collection ? $items : Collection::make($items); return new LengthAwarePaginator($items->forPage($page, $perPage)->values(), $items->count(), $perPage, $page); }
The paginate
function lets us handle pagination just like we would with Eloquent collections.
Wrapping Up
With this setup, you now have a fully functional mock user controller in Laravel! This mock controller can be extended with additional endpoints or adapted to handle more complex mock data requirements. Whether you’re building a front-end prototype or testing Laravel API responses, this approach can save you time and simplify your development process.
You can get the complete code here on Github
<?php | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
use Illuminate\Pagination\Paginator; | |
use Illuminate\Support\Collection; | |
use Illuminate\Pagination\LengthAwarePaginator; | |
use Illuminate\Support\Str; | |
use Illuminate\Support\Facades\Validator; | |
class UsersController extends Controller | |
{ | |
/** | |
* @OA\Info( | |
* version="1.0.0", | |
* title="Keenthemes API", | |
* description="Keenthemes products Mock API", | |
* ) | |
* | |
* | |
* @OA\Server( | |
* url=L5_SWAGGER_CONST_HOST, | |
* description="Keenthemes server" | |
* ) | |
* | |
* @OA\Tag( | |
* name="Users", | |
* description="API Endpoints of Users" | |
* ) | |
* | |
* | |
*/ | |
public $users; | |
public $properties = ["id", "name", "email", "position", 'role', 'last_login', 'two_steps', 'joined_day', 'online']; | |
function __construct() { | |
$this->users = [ | |
[ | |
'id' => 1, | |
'name' => 'Emma Smith', | |
'avatar' => 'avatars/300-6.jpg', | |
'email' => 'smith@kpmg.com', | |
'position' => 'Art Director', | |
'role' => 'Administrator', | |
'last_login' => 'Yesterday', | |
'two_steps' => false, | |
'joined_day' => "10 Nov 2022, 9:23 pm", | |
'online' => false | |
], | |
[ | |
'id' => 2, | |
'name' => 'Melody Macy', | |
'initials' => ['label' => 'M', 'state' => 'danger'], | |
'email' => 'melody@altbox.com', | |
'position' => 'Marketing Analytic', | |
'role' => 'Analyst', | |
'last_login' => '20 mins ago', | |
'two_steps' => true, | |
'joined_day' => "10 Nov 2022, 8:43 pm", | |
'online' => false | |
], | |
[ | |
'id' => 3, | |
'name' => 'Max Smith', | |
'avatar' => 'avatars/300-1.jpg', | |
'email' => 'max@kt.com', | |
'position' => 'Software Enginer', | |
'role' => 'Developer', | |
'last_login' => '3 days ago', | |
'two_steps' => false, | |
'joined_day' => "22 Sep 2022, 8:43 pm", | |
'online' => false | |
], | |
[ | |
'id' => 4, | |
'name' => 'Sean Bean', | |
'avatar' => 'avatars/300-5.jpg', | |
'email' => 'sean@dellito.com', | |
'position' => 'Web Developer', | |
'role' => 'Support', | |
'last_login' => '5 hours ago', | |
'two_steps' => true, | |
'joined_day' => "21 Feb 2022, 6:43 am", | |
'online' => false | |
], | |
[ | |
'id' => 5, | |
'name' => 'Brian Cox', | |
'avatar' => 'avatars/300-25.jpg', | |
'email' => 'brian@exchange.com', | |
'position' => 'UI/UX Designer', | |
'role' => 'Developer', | |
'last_login' => '2 days ago', | |
'two_steps' => true, | |
'joined_day' => "10 Mar 2022, 9:23 pm", | |
'online' => false | |
], | |
[ | |
'id' => 6, | |
'name' => 'Mikaela Collins', | |
'initials' => ['label' => 'M', 'state' => 'warning'], | |
'email' => 'mik@pex.com', | |
'position' => 'Head Of Marketing', | |
'role' => 'Administrator', | |
'last_login' => '5 days ago', | |
'two_steps' => false, | |
'joined_day' => "20 Dec 2022, 10:10 pm", | |
'online' => false | |
], | |
[ | |
'id' => 7, | |
'name' => 'Francis Mitcham', | |
'avatar' => 'avatars/300-9.jpg', | |
'email' => 'f.mit@kpmg.com', | |
'position' => 'Software Arcitect', | |
'role' => 'Trial', | |
'last_login' => '3 weeks ago', | |
'two_steps' => false, | |
'joined_day' => "10 Nov 2022, 6:43 am", | |
'online' => false | |
], | |
[ | |
'id' => 8, | |
'name' => 'Olivia Wild', | |
'initials' => ['label' => 'O', 'state' => 'danger'], | |
'email' => 'olivia@corpmail.com', | |
'position' => 'System Admin', | |
'role' => 'Administrator', | |
'last_login' => 'Yesterday', | |
'two_steps' => false, | |
'joined_day' => "19 Aug 2022, 11:05 am", | |
'online' => false | |
], | |
[ | |
'id' => 9, | |
'name' => 'Neil Owen', | |
'initials' => ['label' => 'N', 'state' => 'primary'], | |
'email' => 'owen.neil@gmail.com', | |
'position' => 'Account Manager', | |
'role' => 'Analyst', | |
'last_login' => '20 mins ago', | |
'two_steps' => true, | |
'joined_day' => "25 Oct 2022, 10:30 am", | |
'online' => false | |
], | |
[ | |
'id' => 10, | |
'name' => 'Dan Wilson', | |
'avatar' => 'avatars/300-23.jpg', | |
'email' => 'dam@consilting.com', | |
'position' => 'Web Desinger', | |
'role' => 'Developer', | |
'last_login' => '3 days ago', | |
'two_steps' => false, | |
'joined_day' => "19 Aug 2022, 10:10 pm", | |
'online' => false | |
], | |
[ | |
'id' => 11, | |
'name' => 'Emma Bold', | |
'initials' => ['label' => 'E', 'state' => 'danger'], | |
'email' => 'emma@intenso.com', | |
'position' => 'Corporate Finance', | |
'role' => 'Support', | |
'last_login' => '5 hours ago', | |
'two_steps' => true, | |
'joined_day' => "20 Dec 2022, 11:05 am", | |
'online' => true | |
], | |
[ | |
'id' => 12, | |
'name' => 'Ana Crown', | |
'avatar' => 'avatars/300-12.jpg', | |
'email' => 'ana.cf@limtel.com', | |
'position' => 'Customer Relationship', | |
'role' => 'Developer', | |
'last_login' => '2 days ago', | |
'two_steps' => true, | |
'joined_day' => "20 Dec 2022, 10:10 pm", | |
'online' => false | |
], | |
[ | |
'id' => 13, | |
'name' => 'Robert Doe', | |
'initials' => ['label' => 'R', 'state' => 'info'], | |
'email' => 'robert@benko.com', | |
'position' => 'Marketing Executive', | |
'role' => 'Administrator', | |
'last_login' => '5 days ago', | |
'two_steps' => false, | |
'joined_day' => "05 May 2022, 10:30 am", | |
'online' => false | |
], | |
[ | |
'id' => 14, | |
'name' => 'John Miller', | |
'avatar' => 'avatars/300-13.jpg', | |
'email' => 'miller@mapple.com', | |
'position' => 'Project Manager', | |
'role' => 'Trial', | |
'last_login' => '3 weeks ago', | |
'two_steps' => false, | |
'joined_day' => "25 Jul 2022, 11:30 am", | |
'online' => false | |
], | |
[ | |
'id' => 15, | |
'name' => 'Lucy Kunic', | |
'initials' => ['label' => 'L', 'state' => 'success'], | |
'email' => 'lucy.m@fentech.com', | |
'position' => 'SEO Master', | |
'role' => 'Administrator', | |
'last_login' => 'Yesterday', | |
'two_steps' => false, | |
'joined_day' => "05 May 2022, 6:05 pm", | |
'online' => false | |
], | |
[ | |
'id' => 16, | |
'name' => 'Minnie Cooper', | |
'initials' => ['label' => 'M', 'state' => 'primary'], | |
'email' => 'minnie.cooper@kt.com', | |
'position' => 'Accountant', | |
'role' => 'Analyst', | |
'last_login' => '20 mins ago', | |
'two_steps' => true, | |
'joined_day' => "20 Dec 2022, 5:20 pm", | |
'online' => false | |
], | |
[ | |
'id' => 17, | |
'name' => 'Annette Smith', | |
'avatar' => 'avatars/300-14.jpg', | |
'email' => 'annette.smith@kt.com', | |
'position' => 'Accountant', | |
'role' => 'Developer', | |
'last_login' => '3 days ago', | |
'two_steps' => false, | |
'joined_day' => "24 Jun 2022, 11:30 am", | |
'online' => false | |
], | |
[ | |
'id' => 18, | |
'name' => 'Kristen Webb', | |
'avatar' => 'avatars/300-18.jpg', | |
'email' => 'kristen.webb@dellito.com', | |
'position' => 'Accountant', | |
'role' => 'Support', | |
'last_login' => '5 hours ago', | |
'two_steps' => true, | |
'joined_day' => "22 Sep 2022, 6:05 pm", | |
'online' => false | |
], | |
[ | |
'id' => 19, | |
'name' => 'Vicki Moreno', | |
'avatar' => 'avatars/300-22.jpg', | |
'email' => 'vicki.moreno@exchange.com', | |
'position' => 'Accountant', | |
'role' => 'Developer', | |
'last_login' => '2 days ago', | |
'two_steps' => true, | |
'joined_day' => "25 Oct 2022, 10:30 am", | |
'online' => false | |
], | |
[ | |
'id' => 20, | |
'name' => 'Lucas Hicks', | |
'initials' => ['label' => 'L', 'state' => 'danger'], | |
'email' => 'lucas.hicks@pex.com', | |
'position' => 'Head Of Marketing', | |
'role' => 'Administrator', | |
'last_login' => '5 days ago', | |
'two_steps' => false, | |
'joined_day' => "19 Aug 2022, 11:05 am", | |
'online' => false | |
], | |
[ | |
'id' => 21, | |
'name' => 'Nellie Jones', | |
'avatar' => 'avatars/300-24.jpg', | |
'email' => 'nellie.jones@kpmg.com', | |
'position' => 'Software Arcitect', | |
'role' => 'Trial', | |
'last_login' => '3 weeks ago', | |
'two_steps' => false, | |
'joined_day' => "10 Nov 2022, 5:20 pm", | |
'online' => false | |
], | |
]; | |
} | |
/** | |
* @OA\Get( | |
* path="/user/{id}", | |
* tags={"Users"}, | |
* description="Get user by id.", | |
* @OA\Parameter( | |
* name="id", | |
* description="User id", | |
* required=true, | |
* in="path", | |
* @OA\Schema( | |
* type="integer" | |
* ) | |
* ), | |
* @OA\Response(response="200", description="User by provided id.", @OA\JsonContent()), | |
* @OA\Response(response="404", description="User with the provided id was not found.", @OA\JsonContent()) | |
* ) | |
*/ | |
function getUser(String $id){ | |
$user = null; | |
foreach ($this->users as $object) { | |
if($object["id"] == $id){ | |
$user = $object; | |
} | |
} | |
if(!$user){ | |
return response(["payload"=>["message" => "User with id ".$id." was not found."]], 404); | |
} | |
return response(["data"=>$user], 200); | |
} | |
/** | |
* @OA\Put( | |
* path="/user", | |
* tags={"Users"}, | |
* description="Add new user.", | |
* @OA\RequestBody( | |
* required=true, | |
* @OA\MediaType( | |
* mediaType="raw", | |
* @OA\Schema( | |
* type="json", | |
* @OA\Property( | |
* property="name", | |
* type="string" | |
* ), | |
* @OA\Property( | |
* property="email", | |
* type="string" | |
* ), | |
* @OA\Property( | |
* property="role", | |
* type="string" | |
* ), | |
* example={"name": "James Parker", "email": "j.parker@kt.com", "role": "Administrator"} | |
* ) | |
* ) | |
* ), | |
* @OA\Response(response="422", description="Not all required fields are provided.", @OA\JsonContent()), | |
* @OA\Response(response="200", description="User has been successfully added.", @OA\JsonContent()) | |
* ) | |
*/ | |
function addUser(Request $request){ | |
$data = json_decode($request->getContent(), true); | |
$validationResult = Validator::make($data, [ | |
'name' => 'required|string|max:255', | |
'avatar' => 'string|max:255', | |
'email' => 'required|string|email|max:255', | |
'role' => 'required|string|max:255', | |
]); | |
if(count($validationResult->errors())!=0) { | |
return response(["payload"=>["message"=>"The given data was invalid.", "errors"=>$validationResult->errors()]], 422); | |
} | |
$data["id"] = count($this->users)+1; | |
array_push($this->users, $data); | |
return response(["data" => $data], 200); | |
} | |
/** | |
* @OA\Post( | |
* path="/user/{id}", | |
* tags={"Users"}, | |
* description="Update user info by id.", | |
* @OA\Parameter( | |
* name="id", | |
* description="User id", | |
* required=true, | |
* in="path", | |
* @OA\Schema( | |
* type="integer" | |
* ) | |
* ), | |
* @OA\RequestBody( | |
* required=true, | |
* @OA\MediaType( | |
* mediaType="raw", | |
* @OA\Schema( | |
* type="json", | |
* @OA\Property( | |
* property="name", | |
* type="string" | |
* ), | |
* @OA\Property( | |
* property="email", | |
* type="string" | |
* ), | |
* @OA\Property( | |
* property="role", | |
* type="string" | |
* ), | |
* example={"name": "James Parker", "email": "j.parker@kt.com", "role": "Administrator"} | |
* ) | |
* ) | |
* ), | |
* @OA\Response(response="422", description="Not all required filed are provided.", @OA\JsonContent()), | |
* @OA\Response(response="200", description="Users data has been successfully updated.", @OA\JsonContent()) | |
* ) | |
*/ | |
function updateUser(Request $request, String $id){ | |
$data = json_decode($request->getContent(), true); | |
$validationResult = Validator::make($data, [ | |
'name' => 'required|string|max:255', | |
'avatar' => 'string|max:255', | |
'email' => 'required|string|email|max:255', | |
'role' => 'required|string|max:255', | |
]); | |
if(count($validationResult->errors())!=0) { | |
return response(["payload"=>["message"=>"The given data was invalid.", "errors"=>$validationResult->errors()]], 422); | |
} | |
$updateUser = []; | |
foreach ($this->users as $object) { | |
if($object["id"] == $id){ | |
if(array_key_exists("initials", $object) && array_key_exists("avatar", $data)){ | |
unset($object["initials"]); | |
$object["avatar"] = $data["avatar"]; | |
} | |
$updateUser = (object) array_merge((array) $object, (array) $data); | |
} | |
} | |
return response(["data" => $updateUser], 200); | |
} | |
/** | |
* @OA\Delete( | |
* path="/user/{id}", | |
* tags={"Users"}, | |
* description="Delete user by id.", | |
* @OA\Parameter( | |
* name="id", | |
* description="User id", | |
* required=true, | |
* in="path", | |
* @OA\Schema( | |
* type="integer" | |
* ) | |
* ), | |
* @OA\Response(response="200", description="User has been successfully deleted.", @OA\JsonContent()), | |
* @OA\Response(response="404", description="User with provided id was not found.", @OA\JsonContent()) | |
* ) | |
*/ | |
function deleteUser($id){ | |
$userFound = false; | |
$usersCollection = collect($this->users); | |
foreach($usersCollection as $key => $item){ | |
if($item['id'] == $id){ | |
$userFound = true; | |
unset($usersCollection[$key]); | |
} | |
} | |
if(!$userFound){ | |
return response(["payload"=>["message" => "User with id ".$id." was not found."]], 404); | |
} | |
return response(200); | |
} | |
/** | |
* @OA\Get( | |
* path="/users/query", | |
* tags={"Users"}, | |
* description="Get user query.", | |
* @OA\Parameter( | |
* name="page", | |
* description="Pagination current page", | |
* in="query", | |
* @OA\Schema( | |
* type="integer" | |
* ) | |
* ), | |
* @OA\Parameter( | |
* name="items_per_page", | |
* description="Items per page", | |
* in="query", | |
* @OA\Schema( | |
* type="integer" | |
* ) | |
* ), | |
* @OA\Parameter( | |
* name="search", | |
* description="Search key.", | |
* in="query", | |
* @OA\Schema( | |
* type="string" | |
* ) | |
* ), | |
* @OA\Parameter( | |
* name="sort", | |
* description="Sort label.", | |
* in="query", | |
* @OA\Schema( | |
* type="string" | |
* ) | |
* ), | |
* @OA\Parameter( | |
* name="order", | |
* description="Sort order asc/desc.", | |
* in="query", | |
* @OA\Schema( | |
* type="string" | |
* ) | |
* ), | |
* @OA\Response(response="200", description="List of the users.", @OA\JsonContent()) | |
* ) | |
* | |
* @OA\Get( | |
* path="/users/query?filter_online=false", | |
* tags={"Users"}, | |
* description="Get filterd users.", | |
* @OA\Response(response="200", description="List of the users.", @OA\JsonContent()), | |
* ) | |
*/ | |
function getUsers(Request $request){ | |
$searchableFields = ["name", "email", "role", "last_login"]; | |
$usersCollection = collect($this->users); | |
$filters = collect([]); | |
$search = $request->input('search'); | |
$currentPage = $request->input('page') ? $request->input('page') : 1; | |
$itemPerPage = $request->input("items_per_page") ? $request->input("items_per_page") : 10; | |
$sortLabel = $request->input("sort"); | |
$order = $request->input("order"); | |
foreach ($this->properties as $value) { | |
$filterValue = $request->input("filter_".$value); | |
if($filterValue){ | |
if($value === 'online' || $value === 'two_steps'){ | |
$filters[$value] = $filterValue === 'true' ? true : false; | |
} else { | |
$filters[$value] = $filterValue; | |
} | |
} | |
} | |
if($order === "desc"){ | |
$usersCollection = $usersCollection->sortByDesc($sortLabel); | |
} else { | |
$usersCollection = $usersCollection->sortBy($sortLabel); | |
} | |
foreach ($filters as $key => $value) { | |
$usersCollection = $usersCollection->filter(function ($item) use ($key, $value) { | |
return strtolower($item[$key]) == strtolower($value); | |
}); | |
} | |
$pagination = $usersCollection; | |
if($search){ | |
$searchedArray = []; | |
foreach($usersCollection as $item){ | |
foreach($searchableFields as $field){ | |
if(strpos(strtolower($item[$field]), strtolower($search)) !== false){ | |
array_push($searchedArray, $item); | |
break; | |
} | |
} | |
} | |
$pagination = $searchedArray; | |
} | |
$paginatedUsers = $this->paginate($pagination, $itemPerPage)->toArray(); | |
$newLinks = []; | |
foreach ($paginatedUsers["links"] as $element) { | |
if($element["url"]){ | |
$element["page"] = (int) str_replace("/?page=","", $element["url"]); ; | |
} else { | |
$element["page"] = null; | |
} | |
array_push($newLinks, $element); | |
} | |
return response([ | |
"data" => $paginatedUsers["data"], | |
"payload" => [ | |
"pagination" => [ | |
"page"=> $paginatedUsers["current_page"], | |
"first_page_url"=> $paginatedUsers["first_page_url"], | |
"from"=> $paginatedUsers["from"], | |
"last_page"=> $paginatedUsers["last_page"], | |
"links"=> $newLinks, | |
"next_page_url"=> $paginatedUsers["next_page_url"], | |
"items_per_page"=> $paginatedUsers["per_page"], | |
"prev_page_url"=> $paginatedUsers["prev_page_url"], | |
"to"=> $paginatedUsers["to"], | |
"total"=> $paginatedUsers["total"] | |
] | |
] | |
], 200); | |
} | |
public function paginate($items, $perPage = 5, $page = null, $options = []) | |
{ | |
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1); | |
$items = $items instanceof Collection ? $items : Collection::make($items); | |
return new LengthAwarePaginator($items->forPage($page, $perPage)->values()->all(), $items->count(), $perPage, $page, $options); | |
} | |
} |