Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 71 additions & 1 deletion docs/en/quickstart.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,52 @@
# Quick Start Guide

The best way to experience and learn CakePHP is to sit down and build something.
To start off we'll build a simple Content Management application.
In this tutorial, we'll build a simple **Content Management System (CMS)** application that demonstrates the core features of CakePHP.

## What You'll Build

By the end of this tutorial, you'll have a fully functional CMS with:

- ✅ **User Authentication** - Secure login system
- ✅ **Article Management** - Create, read, update, and delete articles
- ✅ **Tag System** - Organize content with tags
- ✅ **Database Relations** - Users, articles, and tags working together
- ✅ **Form Validation** - Input validation and error handling
- ✅ **Clean URLs** - SEO-friendly slug-based routing

::: tip Estimated Time
This tutorial takes approximately **45-60 minutes** to complete from start to finish.
:::

## What You'll Learn

This hands-on tutorial covers:

- Setting up a CakePHP application
- Database migrations and schema management
- Creating models with the ORM
- Building controllers and actions
- Rendering views and templates
- Form handling and validation
- Authentication and authorization
- Working with associations (relationships)

::: details Prerequisites
Before starting, make sure you have:

| Requirement | Version |
|-------------|---------|
| PHP | |minphpversion| - |phpversion| |
| Composer | Latest |
| Database | MySQL 5.7+, PostgreSQL 9.6+, or SQLite 3 |
| Web Server | PHP built-in server (for development) |

**Basic PHP knowledge is recommended** but not strictly required.
:::

## Getting Started

Let's begin by installing CakePHP and setting up your development environment.

<!--@include: tutorials-and-examples/cms/installation.md-->

Expand All @@ -10,3 +55,28 @@ To start off we'll build a simple Content Management application.
<!--@include: tutorials-and-examples/cms/articles-model.md-->

<!--@include: tutorials-and-examples/cms/articles-controller.md-->

## What's Next?

🎉 **Congratulations!** You've built your first CakePHP application and learned the fundamentals of the framework.

### Continue Building

Ready to enhance your CMS? Here are some features you could add next:

- **[Authentication & Authorization](../tutorials-and-examples/cms/authentication)** - Secure your application with user login
- **[Tags & Categories](../tutorials-and-examples/cms/tags-and-users)** - Add tagging functionality to organize articles

### Explore CakePHP Features

Dive deeper into CakePHP's powerful features:

- **[ORM & Database](../orm)** - Advanced queries, associations, and behaviors
- **[Validation](../core-libraries/validation)** - Complex validation rules and custom validators
- **[Security](../security)** - CSRF protection, encryption, and security best practices
- **[Testing](../development/testing)** - Write unit and integration tests
- **[Deployment](../deployment)** - Deploy your application to production

### Get Help & Connect

<!--@include: index.md#get-help-->
98 changes: 53 additions & 45 deletions docs/en/tutorials-and-examples/cms/articles-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ methods, to prepare the response. We'll place this new controller in a file
called **ArticlesController.php** inside the **src/Controller** directory.
Here's what the basic controller should look like:

``` php
``` php {3}
<?php
// src/Controller/ArticlesController.php
declare(strict_types=1);

namespace App\Controller;

Expand All @@ -25,15 +26,16 @@ have routes connected to them. For example, when a user requests
a response by rendering a Template in the View. The code for that action would
look like this:

``` php
``` php {3,9}
<?php
// src/Controller/ArticlesController.php
declare(strict_types=1);

namespace App\Controller;

class ArticlesController extends AppController
{
public function index()
public function index(): void
{
$articles = $this->paginate($this->Articles);
$this->set(compact('articles'));
Expand Down Expand Up @@ -126,10 +128,10 @@ correctly formatted with the title and table listing of the articles.
If you were to click one of the 'view' links in our Articles list page, you'd
see an error page saying that action hasn't been implemented. Lets fix that now:

``` php
``` php {3}
// Add to existing src/Controller/ArticlesController.php file

public function view($slug = null)
public function view(?string $slug): void
{
$article = $this->Articles->findBySlug($slug)->firstOrFail();
$this->set(compact('article'));
Expand All @@ -153,7 +155,7 @@ page telling us we're missing a view template; let's fix that.
Let's create the view for our new 'view' action and place it in
**templates/Articles/view.php**

``` php
``` php {3-4}
<!-- File: templates/Articles/view.php -->

<h1><?= h($article->title) ?></h1>
Expand All @@ -162,6 +164,10 @@ Let's create the view for our new 'view' action and place it in
<p><?= $this->Html->link('Edit', ['action' => 'edit', $article->slug]) ?></p>
```

::: tip Security: XSS Protection
The `h()` helper function escapes output to prevent XSS (Cross-Site Scripting) attacks. Always use it when outputting user-generated content.
:::

You can verify that this is working by trying the links at `/articles/index` or
manually requesting an article by accessing URLs like
`/articles/view/first-post`.
Expand All @@ -172,28 +178,30 @@ With the basic read views created, we need to make it possible for new articles
to be created. Start by creating an `add()` action in the
`ArticlesController`. Our controller should now look like:

``` php
``` php {3,11,17,23}
<?php
// src/Controller/ArticlesController.php
declare(strict_types=1);

namespace App\Controller;

use App\Controller\AppController;

class ArticlesController extends AppController
{
public function index()
public function index(): void
{
$articles = $this->paginate($this->Articles);
$this->set(compact('articles'));
}

public function view($slug)
public function view(?string $slug): void
{
$article = $this->Articles->findBySlug($slug)->firstOrFail();
$this->set(compact('article'));
}

public function add()
public function add(): void
{
$article = $this->Articles->newEmptyEntity();
if ($this->request->is('post')) {
Expand All @@ -215,10 +223,11 @@ class ArticlesController extends AppController
}
```

> [!NOTE]
> You need to include the [Flash](../../controllers/components/flash) component in
> any controller where you will use it. Often it makes sense to include it in
> your `AppController`, which is there already for this tutorial.
::: info Flash Component
You need to include the [Flash](../../controllers/components/flash) component in
any controller where you will use it. Often it makes sense to include it in
your `AppController`, which is there already for this tutorial.
:::

Here's what the `add()` action does:

Expand Down Expand Up @@ -304,15 +313,15 @@ creating a slug attribute, and the column is `NOT NULL`. Slug values are
typically a URL-safe version of an article's title. We can use the
[beforeSave() callback](../../orm/table-objects#table-callbacks) of the ORM to populate our slug:

``` php
``` php {3,7-9,13}
<?php
// in src/Model/Table/ArticlesTable.php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;
// the Text class
use Cake\Utility\Text;
// the EventInterface class
use Cake\Event\EventInterface;

// Add the following method.
Expand All @@ -335,12 +344,10 @@ fix that later on.
Our application can now save articles, but we can't edit them. Lets rectify that
now. Add the following action to your `ArticlesController`:

``` php
``` php {3}
// in src/Controller/ArticlesController.php

// Add the following method.

public function edit($slug)
public function edit(?string $slug): void
{
$article = $this->Articles
->findBySlug($slug)
Expand Down Expand Up @@ -430,11 +437,11 @@ articles:
Up until this point our Articles had no input validation done. Lets fix that by
using [a validator](../../orm/validation#validating-request-data):

``` php
``` php {5,8}
// src/Model/Table/ArticlesTable.php

// add this use statement right below the namespace declaration to import
// the Validator class
// add this use statement right below the namespace declaration
// to import the Validator class
use Cake\Validation\Validator;

// Add the following method.
Expand Down Expand Up @@ -472,12 +479,10 @@ automatically.
Next, let's make a way for users to delete articles. Start with a
`delete()` action in the `ArticlesController`:

``` php
``` php {3}
// src/Controller/ArticlesController.php

// Add the following method.

public function delete($slug)
public function delete(?string $slug): void
{
$this->request->allowMethod(['post', 'delete']);

Expand All @@ -499,10 +504,11 @@ error page is displayed. There are many built-in
[Exceptions](../../development/errors) that can be used to indicate the various
HTTP errors your application might need to generate.

> [!WARNING]
> Allowing content to be deleted using GET requests is *very* dangerous, as web
> crawlers could accidentally delete all your content. That is why we used
> `allowMethod()` in our controller.
::: danger Security Warning
Allowing content to be deleted using GET requests is *very* dangerous, as web
crawlers could accidentally delete all your content. That is why we used
`allowMethod()` in our controller.
:::

Because we're only executing logic and redirecting to another action, this
action has no template. You might want to update your index template with links
Expand Down Expand Up @@ -548,19 +554,21 @@ Using `Cake\View\Helper\FormHelper::deleteLink()` will create a link
that uses JavaScript to do a DELETE request deleting our article.
Prior to CakePHP 5.2 you need to use `postLink()` instead.

> [!NOTE]
> This view code also uses the `FormHelper` to prompt the user with a
> JavaScript confirmation dialog before they attempt to delete an
> article.

> [!TIP]
> The `ArticlesController` can also be built with `bake`:
>
> ``` bash
> /bin/cake bake controller articles
> ```
>
> However, this does not build the **templates/Articles/\*.php** files.
::: info FormHelper Confirmation
This view code also uses the `FormHelper` to prompt the user with a
JavaScript confirmation dialog before they attempt to delete an
article.
:::

::: tip Use Bake to Generate Controllers
The `ArticlesController` can also be built with `bake`:

``` bash
bin/cake bake controller articles
```

However, this does not build the **templates/Articles/\*.php** files.
:::

With a basic articles management setup, we'll create the [basic actions
for our Tags and Users tables](../../tutorials-and-examples/cms/tags-and-users).
32 changes: 17 additions & 15 deletions docs/en/tutorials-and-examples/cms/articles-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ They are stored in **src/Model/Table**. The file we'll be creating will be saved
to **src/Model/Table/ArticlesTable.php**. The completed file should look like
this:

``` php
``` php {3,11}
<?php
// src/Model/Table/ArticlesTable.php
declare(strict_types=1);
Expand All @@ -36,19 +36,20 @@ By naming our Table object `ArticlesTable`, CakePHP can use naming conventions
to know that our model uses the `articles` table. CakePHP also uses
conventions to know that the `id` column is our table's primary key.

> [!NOTE]
> CakePHP will dynamically create a model object for you if it
> cannot find a corresponding file in **src/Model/Table**. This also means
> that if you accidentally name your file wrong (i.e. articlestable.php or
> ArticleTable.php), CakePHP will not recognize any of your settings and will
> use the generated model instead.
::: info Automatic Model Creation
CakePHP will dynamically create a model object for you if it
cannot find a corresponding file in **src/Model/Table**. This also means
that if you accidentally name your file wrong (i.e. articlestable.php or
ArticleTable.php), CakePHP will not recognize any of your settings and will
use the generated model instead.
:::

We'll also create an Entity class for our Articles. Entities represent a single
record in the database and provide row-level behavior for our data. Our entity
will be saved to **src/Model/Entity/Article.php**. The completed file should
look like this:

``` php
``` php {3,11}
<?php
// src/Model/Entity/Article.php
declare(strict_types=1);
Expand Down Expand Up @@ -77,13 +78,14 @@ Right now, our entity is quite slim; we've only set up the `_accessible`
property, which controls how properties can be modified by
[Entities Mass Assignment](../../orm/entities#entities-mass-assignment).

> [!TIP]
> The `ArticlesTable` and `Article` Entity classes can be generated from a
> terminal:
>
> ``` bash
> bin/cake bake model articles
> ```
::: tip Use Bake to Generate Models
The `ArticlesTable` and `Article` Entity classes can be generated from a
terminal:

``` bash
bin/cake bake model articles
```
:::

We can't do much with this model yet. Next, we'll create our first
[Controller and Template](../../tutorials-and-examples/cms/articles-controller)
Expand Down
Loading