diff --git a/user_guide_src/source/images/tutorial9.png b/user_guide_src/source/images/tutorial9.png deleted file mode 100644 index 39986162c65b..000000000000 Binary files a/user_guide_src/source/images/tutorial9.png and /dev/null differ diff --git a/user_guide_src/source/tutorial/create_news_items.rst b/user_guide_src/source/tutorial/create_news_items.rst index 2c49a17c2f0a..4ed6badbae07 100644 --- a/user_guide_src/source/tutorial/create_news_items.rst +++ b/user_guide_src/source/tutorial/create_news_items.rst @@ -3,7 +3,7 @@ Create News Items .. contents:: :local: - :depth: 2 + :depth: 3 You now know how you can read data from a database using CodeIgniter, but you haven't written any information to the database yet. In this section, @@ -29,33 +29,17 @@ You can read more about the CSRF protection in :doc:`Security <../libraries/secu Create a Form ************* -View -==== +Create news/create View File +============================ To input data into the database, you need to create a form where you can input the information to be stored. This means you'll be needing a form with two fields, one for the title and one for the text. You'll derive -the slug from our title in the model. Create a new view at -**app/Views/news/create.php**:: +the slug from our title in the model. -

+Create a new view at **app/Views/news/create.php**: - getFlashdata('error') ?> - - -
- - - - -
- - - -
- - -
+.. literalinclude:: create_news_items/006.php There are probably only four things here that look unfamiliar. @@ -72,43 +56,61 @@ The :php:func:`csrf_field()` function creates a hidden input with a CSRF token t The :php:func:`set_value()` function provided by the :doc:`../helpers/form_helper` is used to show old input data when errors occur. -Controller -========== +News Controller +=============== + +Go back to your ``News`` controller. + +Add News::new() to Display the Form +----------------------------------- -Go back to your **News** controller. You're going to do two things here, -check whether the form was submitted and whether the submitted data -passed the validation rules. -You'll use the :ref:`validation method in Controller ` to do this. +First, create a method to display the HTML form you have created. .. literalinclude:: create_news_items/002.php -The code above adds a lot of functionality. +We load the :doc:`Form helper <../helpers/form_helper>` with the +:php:func:`helper()` function. Most helper functions require the helper to be +loaded before use. + +Then it returns the created form view. + +Add News::create() to Create a News Item +---------------------------------------- -First we load the :doc:`Form helper <../helpers/form_helper>` with the :php:func:`helper()` function. -Most helper functions require the helper to be loaded before use. +Next, create a method to create a news item from the submitted data. -Next, we check if we deal with the **POST** request with the -:doc:`IncomingRequest <../incoming/incomingrequest>` object ``$this->request``. -It is set in the controller by the framework. -The :ref:`IncomingRequest::is() ` method checks the type of the request. -Since the route for **create()** endpoint handles both: **GET** and **POST** requests we can safely assume that if the request is not POST then it is a GET type. -the form is loaded and returned to display. +You're going to do three things here: -Then, we get the necessary items from the POST data by the user and set them in the ``$post`` variable. -We also use the :doc:`IncomingRequest <../incoming/incomingrequest>` object ``$this->request``. +1. checks whether the submitted data passed the validation rules. +2. saves the news item to the database. +3. returns a success page. + +.. literalinclude:: create_news_items/005.php + +The code above adds a lot of functionality. -After that, the Controller-provided helper function :ref:`validateData() ` -is used to validate ``$post`` data. +Validate the Data +^^^^^^^^^^^^^^^^^ + +You'll use the Controller-provided helper function :ref:`validate() ` to validate the submitted data. In this case, the title and body fields are required and in the specific length. CodeIgniter has a powerful validation library as demonstrated above. You can read more about the :doc:`Validation library <../libraries/validation>`. -If the validation fails, the form is loaded and returned to display. +If the validation fails, we call the ``new()`` method you just created and return +the HTML form. + +Save the News Item +^^^^^^^^^^^^^^^^^^ + +If the validation passed all the rules, we get the validated data by +:ref:`$this->validator->getValidated() ` and +set them in the ``$post`` variable. -If the validation passed all the rules, the **NewsModel** is loaded and called. This -takes care of passing the news item into the model. The :ref:`model-save` method handles -inserting or updating the record automatically, based on whether it finds an array key -matching the primary key. +The ``NewsModel`` is loaded and called. This takes care of passing the news item +into the model. The :ref:`model-save` method handles inserting or updating the +record automatically, based on whether it finds an array key matching the primary +key. This contains a new function :php:func:`url_title()`. This function - provided by the :doc:`URL helper <../helpers/url_helper>` - strips down @@ -116,26 +118,29 @@ the string you pass it, replacing all spaces by dashes (``-``) and makes sure everything is in lowercase characters. This leaves you with a nice slug, perfect for creating URIs. -After this, view files are loaded and returned to display a success message. Create a view at -**app/Views/news/success.php** and write a success message. +Return Success Page +^^^^^^^^^^^^^^^^^^^ + +After this, view files are loaded and returned to display a success message. +Create a view at **app/Views/news/success.php** and write a success message. This could be as simple as::

News item created successfully.

-Model Updating -************** +NewsModel Updating +****************** The only thing that remains is ensuring that your model is set up to allow data to be saved properly. The ``save()`` method that was used will determine whether the information should be inserted or if the row already exists and should be updated, based on the presence of a primary key. In this case, there is no ``id`` field passed to it, -so it will insert a new row into it's table, **news**. +so it will insert a new row into it's table, ``news``. However, by default the insert and update methods in the Model will not actually save any data because it doesn't know what fields are -safe to be updated. Edit the **NewsModel** to provide it a list of updatable +safe to be updated. Edit the ``NewsModel`` to provide it a list of updatable fields in the ``$allowedFields`` property. .. literalinclude:: create_news_items/003.php @@ -146,19 +151,28 @@ never need to do that, since it is an auto-incrementing field in the database. This helps protect against Mass Assignment Vulnerabilities. If your model is handling your timestamps, you would also leave those out. -Routing -******* +Adding Routing Rules +******************** Before you can start adding news items into your CodeIgniter application you have to add an extra rule to **app/Config/Routes.php** file. Make sure your -file contains the following. This makes sure CodeIgniter sees ``create()`` -as a method instead of a news item's slug. You can read more about different -routing types in :doc:`../incoming/routing`. +file contains the following: .. literalinclude:: create_news_items/004.php +The route directive for ``'news/new'`` is placed before the directive for ``'news/(:segment)'`` to ensure that the form to create a news item is displayed. + +The ``$routes->post()`` line defines the router for a POST request. It matches +only a POST request to the URI path **/news**, and it maps to the ``create()`` method of +the ``News`` class. + +You can read more about different routing types in :ref:`defined-route-routing`. + +Create a News Item +****************** + Now point your browser to your local development environment where you -installed CodeIgniter and add ``/news/create`` to the URL. +installed CodeIgniter and add **/news/create** to the URL. Add some news and check out the different pages you made. .. image:: ../images/tutorial3.png @@ -176,9 +190,29 @@ Congratulations You just completed your first CodeIgniter4 application! -The image underneath shows your project's **app** folder, -with all of the files that you created in red. -The two modified configuration files (**Config/Routes.php** & **Config/Filters.php**) are not shown. - -.. image:: ../images/tutorial9.png - :align: left +The diagram underneath shows your project's **app** folder, with all of the +files that you created or modified. + +.. code-block:: none + + app/ + ├── Config + │   ├── Filters.php (Modified) + │   └── Routes.php (Modified) + ├── Controllers + │   ├── News.php + │   └── Pages.php + ├── Models + │   └── NewsModel.php + └── Views + ├── news + │   ├── create.php + │   ├── index.php + │   ├── success.php + │   └── view.php + ├── pages + │   ├── about.php + │   └── home.php + └── templates + ├── footer.php + └── header.php diff --git a/user_guide_src/source/tutorial/create_news_items/002.php b/user_guide_src/source/tutorial/create_news_items/002.php index be1ad6a54836..0b196dfa7245 100644 --- a/user_guide_src/source/tutorial/create_news_items/002.php +++ b/user_guide_src/source/tutorial/create_news_items/002.php @@ -3,46 +3,18 @@ namespace App\Controllers; use App\Models\NewsModel; +use CodeIgniter\Exceptions\PageNotFoundException; class News extends BaseController { // ... - public function create() + public function new() { helper('form'); - // Checks whether the form is submitted. - if (! $this->request->is('post')) { - // The form is not submitted, so returns the form. - return view('templates/header', ['title' => 'Create a news item']) - . view('news/create') - . view('templates/footer'); - } - - $post = $this->request->getPost(['title', 'body']); - - // Checks whether the submitted data passed the validation rules. - if (! $this->validateData($post, [ - 'title' => 'required|max_length[255]|min_length[3]', - 'body' => 'required|max_length[5000]|min_length[10]', - ])) { - // The validation fails, so returns the form. - return view('templates/header', ['title' => 'Create a news item']) - . view('news/create') - . view('templates/footer'); - } - - $model = model(NewsModel::class); - - $model->save([ - 'title' => $post['title'], - 'slug' => url_title($post['title'], '-', true), - 'body' => $post['body'], - ]); - return view('templates/header', ['title' => 'Create a news item']) - . view('news/success') + . view('news/create') . view('templates/footer'); } } diff --git a/user_guide_src/source/tutorial/create_news_items/004.php b/user_guide_src/source/tutorial/create_news_items/004.php index 3de06f181d29..6b04f3c66ffd 100644 --- a/user_guide_src/source/tutorial/create_news_items/004.php +++ b/user_guide_src/source/tutorial/create_news_items/004.php @@ -5,10 +5,10 @@ use App\Controllers\News; use App\Controllers\Pages; -$routes->match(['get', 'post'], 'news/create', [News::class, 'create']); -$routes->get('news/(:segment)', [News::class, 'view']); $routes->get('news', [News::class, 'index']); +$routes->get('news/new', [News::class, 'new']); // Add this line +$routes->post('news', [News::class, 'create']); // Add this line +$routes->get('news/(:segment)', [News::class, 'show']); + $routes->get('pages', [Pages::class, 'index']); $routes->get('(:segment)', [Pages::class, 'view']); - -// ... diff --git a/user_guide_src/source/tutorial/create_news_items/005.php b/user_guide_src/source/tutorial/create_news_items/005.php new file mode 100644 index 000000000000..6d565789141a --- /dev/null +++ b/user_guide_src/source/tutorial/create_news_items/005.php @@ -0,0 +1,40 @@ +validate([ + 'title' => 'required|max_length[255]|min_length[3]', + 'body' => 'required|max_length[5000]|min_length[10]', + ])) { + // The validation fails, so returns the form. + return $this->new(); + } + + // Gets the validated data. + $post = $this->validator->getValidated(); + + $model = model(NewsModel::class); + + $model->save([ + 'title' => $post['title'], + 'slug' => url_title($post['title'], '-', true), + 'body' => $post['body'], + ]); + + return view('templates/header', ['title' => 'Create a news item']) + . view('news/success') + . view('templates/footer'); + } +} diff --git a/user_guide_src/source/tutorial/create_news_items/006.php b/user_guide_src/source/tutorial/create_news_items/006.php new file mode 100644 index 000000000000..b19bbfe9b2c5 --- /dev/null +++ b/user_guide_src/source/tutorial/create_news_items/006.php @@ -0,0 +1,18 @@ +

+ +getFlashdata('error') ?> + + +
+ + + + +
+ + + +
+ + +
diff --git a/user_guide_src/source/tutorial/index.rst b/user_guide_src/source/tutorial/index.rst index 85675764eceb..830ab6568f9a 100644 --- a/user_guide_src/source/tutorial/index.rst +++ b/user_guide_src/source/tutorial/index.rst @@ -76,7 +76,7 @@ Setting Development Mode By default, CodeIgniter starts up in production mode. This is a safety feature to keep your site a bit more secure in case settings are messed up once it is live. -So first let's fix that. Copy or rename the ``env`` file to ``.env``. Open it up. +So first let's fix that. Copy or rename the **env** file to **.env**. Open it up. This file contains server-specific settings. This means you never will need to commit any sensitive information to your version control system. It includes diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst index 9fa090701f67..183b2f0c8570 100644 --- a/user_guide_src/source/tutorial/news_section.rst +++ b/user_guide_src/source/tutorial/news_section.rst @@ -51,7 +51,7 @@ The seed records might be something like:: Connect to Your Database ************************ -The local configuration file, ``.env``, that you created when you installed +The local configuration file, **.env**, that you created when you installed CodeIgniter, should have the database property settings uncommented and set appropriately for the database you want to use. Make sure you've configured your database properly as described in :doc:`../database/configuration`:: @@ -71,7 +71,10 @@ are the place where you retrieve, insert, and update information in your database or other data stores. They provide access to your data. You can read more about it in :doc:`../models/model`. -Open up the **app/Models/** directory and create a new file called +Create NewsModel +================ + +Open up the **app/Models** directory and create a new file called **NewsModel.php** and add the following code. .. literalinclude:: news_section/001.php @@ -81,6 +84,9 @@ creates a new model by extending ``CodeIgniter\Model`` and loads the database library. This will make the database class available through the ``$this->db`` object. +Add NewsModel::getNews() Method +=============================== + Now that the database and a model have been set up, you'll need a method to get all of our posts from our database. To do this, the database abstraction layer that is included with CodeIgniter - @@ -101,7 +107,7 @@ query; :doc:`Query Builder <../database/query_builder>` does this for you. The two methods used here, ``findAll()`` and ``first()``, are provided by the ``CodeIgniter\Model`` class. They already know the table to use based on the ``$table`` -property we set in **NewsModel** class, earlier. They are helper methods +property we set in ``NewsModel`` class, earlier. They are helper methods that use the Query Builder to run their commands on the current table, and returning an array of results in the format of your choice. In this example, ``findAll()`` returns an array of array. @@ -112,8 +118,12 @@ Display the News Now that the queries are written, the model should be tied to the views that are going to display the news items to the user. This could be done in our ``Pages`` controller created earlier, but for the sake of clarity, -a new ``News`` controller is defined. Create the new controller at -**app/Controllers/News.php**. +a new ``News`` controller is defined. + +Create News Controller +====================== + +Create the new controller at **app/Controllers/News.php**. .. literalinclude:: news_section/003.php @@ -126,7 +136,7 @@ access to the current ``Request`` and ``Response`` objects, as well as the Next, there are two methods, one to view all news items, and one for a specific news item. -Next, the :php:func:`model()` function is used to create the **NewsModel** instance. +Next, the :php:func:`model()` function is used to create the ``NewsModel`` instance. This is a helper function. You can read more about it in :doc:`../general/common_functions`. You could also write ``$model = new NewsModel();``, if you don't use it. @@ -134,6 +144,9 @@ You can see that the ``$slug`` variable is passed to the model's method in the second method. The model is using this slug to identify the news item to be returned. +Complete News::index() Method +============================= + Now the data is retrieved by the controller through our model, but nothing is displayed yet. The next thing to do is, passing this data to the views. Modify the ``index()`` method to look like this: @@ -143,8 +156,12 @@ the views. Modify the ``index()`` method to look like this: The code above gets all news records from the model and assigns it to a variable. The value for the title is also assigned to the ``$data['title']`` element and all data is passed to the views. You now need to create a -view to render the news items. Create **app/Views/news/index.php** -and add the next piece of code. +view to render the news items. + +Create news/index View File +=========================== + +Create **app/Views/news/index.php** and add the next piece of code. .. literalinclude:: news_section/005.php @@ -158,11 +175,14 @@ wrote our template in PHP mixed with HTML. If you prefer to use a template language, you can use CodeIgniter's :doc:`View Parser ` or a third party parser. +Complete News::show() Method +============================ + The news overview page is now done, but a page to display individual news items is still absent. The model created earlier is made in such a way that it can easily be used for this functionality. You only need to add some code to the controller and create a new view. Go back to the -``News`` controller and update the ``view()`` method with the following: +``News`` controller and update the ``show()`` method with the following: .. literalinclude:: news_section/006.php @@ -171,23 +191,27 @@ the ``PageNotFoundException`` class. Instead of calling the ``getNews()`` method without a parameter, the ``$slug`` variable is passed, so it will return the specific news item. + +Create news/view View File +========================== + The only thing left to do is create the corresponding view at **app/Views/news/view.php**. Put the following code in this file. .. literalinclude:: news_section/007.php -Routing -******* +Adding Routing Rules +******************** -Modify your routing file -(**app/Config/Routes.php**) so it looks as follows. -This makes sure the requests reach the ``News`` controller instead of -going directly to the ``Pages`` controller. The first line routes URI's -with a slug to the ``view()`` method in the ``News`` controller. +Modify your **app/Config/Routes.php** file, so it looks as follows: .. literalinclude:: news_section/008.php -Point your browser to your "news" page, i.e., ``localhost:8080/news``, +This makes sure the requests reach the ``News`` controller instead of +going directly to the ``Pages`` controller. The second ``$routes->get()`` line +routes URI's with a slug to the ``show()`` method in the ``News`` controller. + +Point your browser to your "news" page, i.e., **localhost:8080/news**, you should see a list of the news items, each of which has a link to display just the one article. diff --git a/user_guide_src/source/tutorial/news_section/003.php b/user_guide_src/source/tutorial/news_section/003.php index 13c3b7263db6..22b24577b01e 100644 --- a/user_guide_src/source/tutorial/news_section/003.php +++ b/user_guide_src/source/tutorial/news_section/003.php @@ -13,7 +13,7 @@ public function index() $data['news'] = $model->getNews(); } - public function view($slug = null) + public function show($slug = null) { $model = model(NewsModel::class); diff --git a/user_guide_src/source/tutorial/news_section/006.php b/user_guide_src/source/tutorial/news_section/006.php index 657211792f46..95e48aa77586 100644 --- a/user_guide_src/source/tutorial/news_section/006.php +++ b/user_guide_src/source/tutorial/news_section/006.php @@ -9,7 +9,7 @@ class News extends BaseController { // ... - public function view($slug = null) + public function show($slug = null) { $model = model(NewsModel::class); diff --git a/user_guide_src/source/tutorial/news_section/008.php b/user_guide_src/source/tutorial/news_section/008.php index 077efd42254d..df11598451a1 100644 --- a/user_guide_src/source/tutorial/news_section/008.php +++ b/user_guide_src/source/tutorial/news_section/008.php @@ -2,12 +2,11 @@ // ... -use App\Controllers\News; +use App\Controllers\News; // Add this line use App\Controllers\Pages; -$routes->get('news/(:segment)', [News::class, 'view']); -$routes->get('news', [News::class, 'index']); +$routes->get('news', [News::class, 'index']); // Add this line +$routes->get('news/(:segment)', [News::class, 'show']); // Add this line + $routes->get('pages', [Pages::class, 'index']); $routes->get('(:segment)', [Pages::class, 'view']); - -// ... diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index d39224f3b018..25b514b51c4b 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -16,6 +16,9 @@ It is the glue of your web application. Let's Make our First Controller ******************************* +Create Pages Controller +======================= + Create a file at **app/Controllers/Pages.php** with the following code. @@ -47,6 +50,9 @@ The **controller is what will become the center of every request** to your web application. Like any PHP class, you refer to it within your controllers as ``$this``. +Create Views +============ + Now that you've created your first method, it's time to make some basic page templates. We will be creating two "views" (page templates) that act as our page footer and header. @@ -80,15 +86,21 @@ includes the following code:: Adding Logic to the Controller ****************************** +Create home.php and about.php +============================= + Earlier you set up a controller with a ``view()`` method. The method -accepts one parameter, which is the name of the page to be loaded. The -static page bodies will be located in the **app/Views/pages/** -directory. +accepts one parameter, which is the name of the page to be loaded. + +The static page bodies will be located in the **app/Views/pages** directory. In that directory, create two files named **home.php** and **about.php**. Within those files, type some text - anything you'd like - and save them. If you like to be particularly un-original, try "Hello World!". +Complete Pages::view() Method +============================= + In order to load those pages, you'll have to check whether the requested page actually exists. This will be the body of the ``view()`` method in the ``Pages`` controller created above: @@ -129,23 +141,22 @@ view. throw errors on case-sensitive platforms. You can read more about it in :doc:`../outgoing/views`. -Routing -******* +Setting Routing Rules +********************* We have made the controller. The next thing is to set routing rules. Routing associates a URI with a controller's method. -Let's do that. Open the routing file located at -**app/Config/Routes.php**. +Let's do that. Open the routes file located at **app/Config/Routes.php**. -The only line there to start with should be: +The only route directive there to start with should be: .. literalinclude:: static_pages/003.php This directive says that any incoming request without any content specified should be handled by the ``index()`` method inside the ``Home`` controller. -Add the following lines, **after** the route directive for '/'. +Add the following lines, **after** the route directive for ``'/'``. .. literalinclude:: static_pages/004.php :lines: 2- @@ -160,7 +171,7 @@ arguments. More information about routing can be found in the :doc:`../incoming/routing`. Here, the second rule in the ``$routes`` object matches a GET request -to the URI path ``/pages``, and it maps to the ``index()`` method of the ``Pages`` class. +to the URI path **/pages**, and it maps to the ``index()`` method of the ``Pages`` class. The third rule in the ``$routes`` object matches a GET request to a URI segment using the placeholder ``(:segment)``, and passes the parameter to the @@ -170,8 +181,8 @@ Running the App *************** Ready to test? You cannot run the app using PHP's built-in server, -since it will not properly process the ``.htaccess`` rules that are provided in -``public``, and which eliminate the need to specify "**index.php/**" +since it will not properly process the **.htaccess** rules that are provided in +**public**, and which eliminate the need to specify "**index.php/**" as part of a URL. CodeIgniter has its own command that you can use though. From the command line, at the root of your project:: @@ -179,9 +190,9 @@ From the command line, at the root of your project:: > php spark serve will start a web server, accessible on port 8080. If you set the location field -in your browser to ``localhost:8080``, you should see the CodeIgniter welcome page. +in your browser to **localhost:8080**, you should see the CodeIgniter welcome page. -Now visit ``localhost:8080/home``. Did it get routed correctly to the ``view()`` +Now visit **localhost:8080/home**. Did it get routed correctly to the ``view()`` method in the ``Pages`` controller? Awesome! You should see something like the following: diff --git a/user_guide_src/source/tutorial/static_pages/003.php b/user_guide_src/source/tutorial/static_pages/003.php index bf0466ca2192..fc4914a6923b 100644 --- a/user_guide_src/source/tutorial/static_pages/003.php +++ b/user_guide_src/source/tutorial/static_pages/003.php @@ -1,7 +1,8 @@ get('/', 'Home::index'); - -// ...