PHPackages                             totallyquiche/crudy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Admin Panels](/categories/admin)
4. /
5. totallyquiche/crudy

ActiveLibrary[Admin Panels](/categories/admin)

totallyquiche/crudy
===================

A proof-of-concept MVC framework.

1.0.1(3y ago)110MITPHPPHP &gt;=8.1

Since Oct 10Pushed 3y ago2 watchersCompare

[ Source](https://github.com/totallyquiche/CRUDy)[ Packagist](https://packagist.org/packages/totallyquiche/crudy)[ RSS](/packages/totallyquiche-crudy/feed)WikiDiscussions main Synced today

READMEChangelog (2)DependenciesVersions (3)Used By (0)

CRUDy
=====

[](#crudy)

[![CRUDy preview](preview.png)](preview.png)

CRUDy is a proof-of-concept PHP framework with no dependencies.

But why?
--------

[](#but-why)

For fun, of course! After all, learning is FUNdamental! 🌈

CRUDy is helping me hone my PHP skills and understand more about the concepts popular PHP frameworks are built on.

CRUDy is *not* meant to be used in production. CRUDy development starts with the naive approach instead of with existing standards (e.g. PSR). I then develop these implementations over time as I learn the pros and cons of different patterns. Compatibility, performance, and even security are not priorities and are not guaranteed.

Feedback is still welcome! You can even open up a Pull Request if you'd like. :smile:

Features
--------

[](#features)

- Autoloading
- Page caching
- Environment variables (.env)
- Database Abstraction Layer (DBAL)
- Headless execution
- MVC architecture
- Routing
- Templates
- Testing

Getting Started
---------------

[](#getting-started)

1. Clone the repo: `git clone git@github.com:totallyquiche/CRUDy.git`
2. Create your config file: `cp .env.example .env`
3. Start your PHP server: `php -S localhost:80`
4. Open `http://localhost` in your browser

Technically, that's it! CRUDy is up and running! But you probably want to do more than that...

### Create a New Page

[](#create-a-new-page)

Add a new route to the `$routes` array in `./App/Http/routes.php`. You can either specify a controller and method or an anonymous function. The method/function you define should return a string (this is what gets rendered).

```
$routes = [
    '/' => 'HomeController::index'
];
```

```
$routes = [
    '/' => fn() => 'Hello, World!'
);
```

### Create a Controller

[](#create-a-controller)

Create a new class in `App/Controllers/Http` that extends `App\Http\Controllers\HttpController`. The class name should end with `Controller` (e.g. `HomeController`). The file name should be the class name plus `.php` (e.g. `HomeController.php`).

The controller should contain methods matching any register routes you have. For example, if I have registerd a route referencing `HomeController::index`, then I should have a controller named `HomeController` with a method named `index()`which returns a string.

### Create a View

[](#create-a-view)

Create a new `.php` file in `App/View/Templates`. Load the view from your controller by calling `$this->renderView($view_name, $args)` in the controller method.

The first parameter is the name of the view (without `.php`). The second parameter is an optional array of data to be passed to the view. The key/value pairs in the array are turned into variables with assigned values.

For example, calling the below method call will result in the the `show_object`view (`App/View/Templates/show_object.php`) being loaded. The view will have access to a variable called `$object_name` with the value `Object Name`;

```
$this->renderView('show_object', [
    'object_name' => 'Object Name';
]);
```

A values in `$args` can also be a `Callable`. For example, calling the method below will give the view access to a variable called `$message` with the value `Hello, World!`:

```
$this->renderView('view_name', [
    'message' => fn() => return 'Hello, World!',
]);
```

Templates
---------

[](#templates)

Templates may be used to support a type of single-inheritance View in which the string `{{ 💩 }}` in the Template is replaced with the contents of the View.

### Using a Template

[](#using-a-template)

To use a Template, ensure that the first line of your View file contains a string like `{{ TEMPLATE_NAME }}`, wherein `TEMPLATE_NAME` is the file name of the Template without `.php`. For example, the following View would utilize a template located at `App\View\Templates\page.php`:

```
{{ page }}
Hello, World!
```

### Creating a Template

[](#creating-a-template)

Templates are files with names matching `[a-zA-Z]+\.php` (e.g. `page.php`) and live in `App\View\Templates`. The should contain a single placeholder, `{{ 💩 }}`for embedding View within them. For example:

```
>

    {{ 💩 }}

```

which, with the following View...

```
{{ page }}
Hello, World!
```

...results in the following being rendered:

```
>

    Hello, World!

```

View Caching
------------

[](#view-caching)

After a View has been fully compiled, it is written to a cache located at `./App/View/Templates/Cache`. That cached file is used each time the corresponding page is requested until the cache period expires (the number of seconds the `VIEW_CACHE_SECONDS_TO_EXPIRY` environment variable is set to).

Testing
-------

[](#testing)

### Writing Tests

[](#writing-tests)

Create a new class in `./App/Tests` that extends `App\Tests\Test`. The class name should end with `Test` (e.g. `HomeControllerTest`). The file name should be the class name plus `.php` (e.g. `HomeControllerTest.php`).

Your test methods should be `public`, should start with `test_`, and should return a `boolean` indicating whether the test passed.

```
public function test_that_true_is_true() : bool
{
    return true === true;
}
```

### Running Tests

[](#running-tests)

As long as you follow the above naming conventions, your tests will be run automatically through the following command:

```
php index.php tests:run
```

You can run specific tests by passing in their name as arguments:

```
php index.php tests:run "App\Tests\ModelTest" "App\Tests\RouterTest"
```

### Test results

[](#test-results)

After running your tests, you will see the results printed to the screen.

Each test cass will be displayed starting with `Passed` or `Failed`. The total pass/fail counts will be displayed at the end.

[![Test results example](test-results-example.png)](test-results-example.png)

###  Health Score

25

—

LowBetter than 35% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

1361d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8302048847fff91f6f56892149ba2a880b4b9f89270e6cbb3ba8bd6e5157ce0b?d=identicon)[totallyquiche](/maintainers/totallyquiche)

---

Top Contributors

[![totallyquiche](https://avatars.githubusercontent.com/u/27497544?v=4)](https://github.com/totallyquiche "totallyquiche (188 commits)")

---

Tags

crudframeworkmvcphp

### Embed Badge

![Health badge](/badges/totallyquiche-crudy/health.svg)

```
[![Health](https://phpackages.com/badges/totallyquiche-crudy/health.svg)](https://phpackages.com/packages/totallyquiche-crudy)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
