Kapcsolat oldal, levélküldés levélszerveren át

Először is telepítsük a sendmail levélszervert és a mail levelező programot, hogy legalább localhost-on le tudjuk tesztelni a levélküldést. Nagyon nem lesz nehéz dolgunk a CodeIgniter-t illetőleg, mivel támogatja a sendmail levelező csomagot.

root@gergo1:~# apt install sendmail
root@gergo1:~# apt-get install mailutils

Ezután teszteljük le a levélküldést. Tegyük fel, hogy fut a sendmail felépítmény (szerver, leírás https://en.wikipedia.org/wiki/Sendmail).

gvamosi@gergo1:/var/www/html/apartman/application$ mail -s "Teszt levél" gvamosi@localhost < /dev/null
mail: Null message body; hope that's ok
gvamosi@gergo1:/var/www/html/apartman/application$ mail
"/var/mail/gvamosi": 1 message 1 new
>N   1 gvamosi            Fri Aug  7 00:46  14/520   Teszt levél
? q
Held 1 message in /var/mail/gvamosi

Láthatjuk, hogy a levelünk “Teszt levél” tárggyal és üres tartalommal elküldésre került és szerencsésen meg is érkezett. 🙂 Helyben került kézbesítve, helyi feladótól. Ne ijedjünk meg, ha picit lassú a kézbesítés, a névfeloldás megy nehezen a sendmail-ünknek, mivel nem vagyunk internetre kötött ún. SMTP host, azaz hivatalos levélküldő szerver. Attól sem kell félnünk, hogy igazi levelet tudunk küldeni a nagyvilágba, ez így lehetetlen. 🙂

Részleteket az internetes levelezésről az alábbi címen találhatunk https://hu.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol.

Mindez szép és jó, de hogyan küldjük el akkor egy webes form-ból a levelet az adminisztrátornak, tehát a kapcsolatfelvételi formot hogyan is programozzuk le?

Vegyünk egy sima, nem JavaScript-es form-ot az egyszerűség kedvéért, majd használjuk a CodeIgniter email könyvtárát. Annyi lesz az egész, hogy be kell konfiguráljuk a sendmail-ünket, és meg kell hívnunk a megfelelő rutinokat a controller-ünkben űrlapküldés után.

Elsőnek hozzuk létre a kapcsolati űrlapot views/contact.php néven!

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li><a href="booking"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li class="active"><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_contact'); ?></h2>
                                </div>
                        </div>
                </div>
                <div id="fh5co-contact">
                        <div class="row">
                                <div class="col-md-8 animate-box">
                                        <div class="form-wrap">
                                                <div class="row">
                                                        <form action="send" method="POST">
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="name" class="form-control" placeholder="Your name">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="email" class="form-control" placeholder="Your email">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <textarea id="message" name="message" class="form-control" id="" cols="30" rows="15" placeholder="Your message"></textarea>
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="submit" value="Send message" class="btn btn-primary btn-modify">
                                                                </div>
                                                        </div>
                                                        </form>
                                                </div>
                                        </div>
                                </div>
                        </div>
                </div>
        </div><!-- END container -->

Látható, hogy ez egy sima form. Nem is fűzök hozzá magyarázatot.

Majd hozzük létre a visszajelző oldalt views/contact_sent.php néven!

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li><a href="booking"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li class="active"><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_contact'); ?></h2>
                                </div>
                        </div>
                </div>
                <div id="fh5co-contact">
                        <div class="row">
                                <div class="col-md-8 animate-box">
                                        <div class="form-wrap">
                                                <div class="row">
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <h3><font color="red">Thank you for contacting us!</font></h3>
                                                                </div>
                                                        </div>
                                                </div>
                                        </div>
                                </div>
                        </div>
                </div>
        </div><!-- END container -->

Ez is beton egyszerű.

Nézzük meg azt a három metódust, amivel a controllers/Apartments.php vezérlőnket bővítettük!

        public function contact() {
                $data = array();

                $this->template->set('title', $this->lang->line('menu_contact'));
                $this->template->load('menu_layout', 'contents', 'contact', $data);
        }

        public function send() {

                $this->load->library('email');

                $config['protocol'] = 'sendmail';
                $config['mailpath'] = '/usr/sbin/sendmail';
                $config['charset'] = 'utf-8';

                $this->email->initialize($config);

                $this->email->from($this->input->post('email'), $this->input->post('name'));
                // mail of administrator
                $this->email->to('gvamosi@localhost');
                $this->email->subject('Mail from booking site');
                $this->email->message($this->input->post('message'));

                $this->email->send();

                redirect(base_url('index.php/apartments/contact_sent'));
        }

        public function contact_sent() {
                $data = array();

                $this->template->set('title', $this->lang->line('menu_contact'));
                $this->template->load('menu_layout', 'contents', 'contact_sent', $data);
        }

A contact() metódus csupán kirajzol egy oldalt, ahogy a contact_sent() is. Az érdekesebb metódusunk a send(), amiben a tulajdonképpeni levélküldés történik. Ennyi az egész! 🙂 A valóságban persze az adminisztrátorok és a fejlesztők együttműködésére és közreműködésére van szükség mind fejlesztés, mind pedig a webalkalmazás telepítése során.

És akkor nézzük a http://localhost/apartman/index.php/apartments/contact oldalunkat!

Kis várakozás után megkapjuk a levelünket. Kettes sorszámmal látható a parancssori levelezőnkben. 🙂

gvamosi@gergo1:/var/www/html/apartman/application$ mail
"/var/mail/gvamosi": 2 messages 1 new 1 unread
 U   1 gvamosi            Fri Aug  7 00:46  17/564   Teszt levél
>N   2 Teszt Elek         Fri Aug  7 01:14  23/823   Mail from booking site
? q
Held 2 messages in /var/mail/gvamosi

Jelszóvédelem az admin oldalra, avagy az authentikáció egyszerű megoldása

Az adminisztrátori vagyis foglalás-menedzsment funkcionalitás eleve rejtve van a kíváncsi tekintetek elől, hiszen ha kint is van az Interneten, vagyis a “webtérben” az oldal, link nincs az admin oldalunkra a honlapon. Azonban ennél azért nagyobb biztonságra lesz szükségünk, ezért az egész admin funkcionalitást el kell rejtsük egy jelszóval védett területre.

A jelszavas védelmet a CodeIgniter keretrendszer alapban nem támogatja, viszont egyszerűen megvalósíthatjuk. Egyszerűen úgy működik, hogy a controller-ben a kívánt funkciók hívása előtt megvizsgáljuk, hogy előzőleg tároltunk-e el a szerver session-jében “user” változót a korábban lezajló login folyamat során, amelyben adatbázisban ellenőrizzük a felhasználó/jelszó párt. Van benne még technikailag pár átirányítás, hogy a webalkalmazásunk “visszataláljon” a “hívó” oldalra (referer) a sikeres jelszó hitelesítés (authentikáció) vagyis belépés (login) után.

És akkor most nézzük a konkrét implementációt.

Először is kell egy modell. Ez az models/Auth_model.php file-unk. Összezsúfoltam picit a kódot – nem szokásom amúgy -, hogy minél kevesebb sorban oldjam meg a feladatot. 🙂

<?php
class Auth_model extends CI_Model {
 public function login(){
  $query = $this->db->get_where('users', array('name' => $this->input->post('username'), 'password' => $this->input->post('password'))); 
  $result = $query->result();
  return (empty($result)?false:(empty($result[0])?false:true));
 }
}

Ez a login() metódus egy boolean – logikai – értékkel tér vissza attól függően, hogy megvan-e az adatbázisban a felhasználó/jelszó páros. Pár technikai észrevétel: a felhasználónévnek egyedinek kell lenni (ezt egy ún. unique key vagyis egyedi kulcs bitosítja SQL technikában), hogy nyilvánvalóan különbözzenek a felhasználóink. A gyakorlatban általában az e-mail címeket választják ki a mérnökök erre a célra, hiszen az mindenképpen egyedi. 🙂 Aztán a jelszó. Nem kódoltam le ebben a pilot alkalmazásban, de illik valamilyen egyirányú kódoló algoritmussal titkosítani, hogy senki se fejthesse vissza a jelszavakat az adatbázis ismeretében. 🙂

Nézzük a meg views/login.php view-nkat.

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li><a href="booking"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_booking'); ?></h2>
                                </div>
                        </div>
                </div>
                <div id="fh5co-contact">
                        <div class="row">
                                <div class="col-md-8 animate-box">
                                        <div class="form-wrap">
                                                <div class="row">
                                                        <form action="login" method="POST">
                                                        <?php if ($auth_errors) { ?>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <h3><font color="red"><?php echo $auth_errors; ?></font></h3>
                                                                </div>
                                                        </div>
                                                        <?php } ?>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="username" class="form-control" placeholder="User Name">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="password" name="password" class="form-control" placeholder="Password">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="submit" value="Login" class="btn btn-primary btn-modify">
                                                                </div>
                                                        </div>
                                                        </form>
                                                </div>
                                        </div>
                                </div>
                        </div>
                </div>
        </div><!-- END container -->

Ebben érdekes, hogy egy sima formot használunk, nem JavaScript-eset, és hogy password típusú mezőbe kérjük be a jelszót, amit nem lehet elolvasni a képernyőn, miközben begépeljük, csak pontokat látni a karakterek helyén. Ez a biztonságot szolgálja. Más kérdés, hogy a billentyűzetről is le tudja olvasni egy ügyes tekintetű illető a jelszót, úgyhogy ilyenkor illik “nem oda nézni” a harmadik félnek. 🙂

Majd nézzük a szintén nem túl bonyolult controller-ünket (controllers/AuthController.php). Megjegyzem, hogy végre beállítottam a config/config.php => $config['base_url'] = 'http://localhost/apartman/'; értéket, hogy használhassam az a base_url($path) metódust a redirect-ek (átirányítások) miatt. 🙂

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class AuthController extends CI_Controller {
  public function __construct(){
    parent::__construct();
  }
  public function is_logged($referer){
    $user = $this->session->userdata('user');
    if (empty($user)) {
      // not authenticated
      $this->session->set_userdata('login_url', $referer);
      redirect(base_url('index.php/AuthController/login_form'));
    }
  }
  public function login_form(){
    $data = array();

    $data['auth_errors'] = '';
    if (!empty($this->session->flashdata('auth_errors'))) {
      $data['auth_errors'] = $this->session->flashdata('auth_errors');
    }

    $this->template->set('title', 'Login');
    $this->template->load('menu_layout', 'contents', 'login', $data);
  }
  public function login(){
   $this->load->model('auth_model');

   $success = $this->auth_model->login(); 

   if ($success) {
     $this->session->set_userdata('user', $this->input->post('username'));
     redirect($this->session->userdata('login_url'));
   } else {
     // login fail
     $this->session->set_flashdata('auth_errors', 'Login failed.');
     redirect($_SERVER['HTTP_REFERER']);
   }
  }
  public function logout(){
    $this->session->set_userdata('login_url', null);
    $this->session->set_userdata('user', null);
  }
}

Látható, hogy három fontos metódust tartamaz. Az is_logged($referer) gondoskodik arról, hogy megvizsgáljuk, van-e belépett felhasználó (user) a session-ben. A login_form() hibaüzenetek kezelését, illetve a login form kirajzolását végzi, míg a login() a tulajdonképpeni beléptetést a modellhívással. A logout() csak játék, technikailag vettem fel. Egyszerűen töröl minden session-ban tárolt változót.

Ezt a controller-t az alkalmazásunk fő vezérlőjében (controllers/Apartments.php) az alábbi módon használjuk.

<?php
include APPPATH.'controllers/AuthController.php';
class Apartments extends AuthController {

        public function admin() {

                // check if user is authenticated
                // config/config.php => $config['base_url'] = 'http://localhost/apartman/';
                $this->is_logged(base_url('index.php/apartments/admin'));
                
                $this->load->model('reservations_model');

                $data['query'] = $this->reservations_model->get_reservations();

                $this->template->set('title', 'Admin');
                $this->template->load('menu_layout', 'contents' , 'admin', $data);
        }

        public function admin_approve_ajax($id) {

                // check if user is authenticated
                $this->is_logged(base_url('index.php/apartments/admin'));

                $this->load->model('reservations_model');

                $this->reservations_model->approve_reservation($id);
                $data = $this->reservations_model->get_reservation($id)[0];

                header('Content-Type: application/json');
                echo json_encode($data);
        }

}

Látható, hogy az AuthController-t include-oljuk a kód elején, majd extends-szel alaposztályként hivatkozunk rá. Innentől egy is_logged($referer) hívással mindkét adminisztrátori metódus legelején eldöntjük, hogy “be van-e lépve” az adminisztrátor. Ha nem, akkor átirányítjuk a login form-ra, ha igen, megy tovább a buli. 🙂

És akkor nézzük a login form-unkat! A http://localhost/apartman/index.php/AuthController/login_form webcím alatt látható, ha az admin oldalt szeretnénk megnyitni, és a session változó már elévült a webszerverünkön vagy netán kipróbáltuk a logout() funkciót. 🙂

Admin funkció, foglalás jóváhagyása, AJAX JSON :)

Most már, hogy egy űrlapon keresztül foglalásokat lehet elküldeni, szükségünk lesz egy admin oldalra, ahol az apartman ügyintézője meg tudja tekinteni, illetve jóvá tudja hagyni a foglalásokat.

Hogy mit jelent itt az hogy AJAX JSON? Elég érdekes öszvér elnevezés. 🙂 AJAX-szal normál esetben egy XML adatstruktúrát vinnénk át az aszinkron javascript üzenetben, ami valahogy így nézne ki egy konkrét foglalás (reservation) adataira.

<xml>
<reservation>
<apartment_id>1</apartment_id>
<approved>1</approved>
..
</reservation>
</xml>

Mégis azonban az ún. JSON formátumot használjuk, ami egy konkrét esetben az alábbi szerverválaszt adja.

{
apartment_id: "1",
approved: "1",
checkin: "2020-07-29",
checkout: "2020-08-01",
created_at: "2020-08-03 03:34:11",
email: "aaa@aaa.hu",
id: "10",
locale: null,
message: "aaa",
name: "aaa",
phone: "",
updated_at: "2020-08-05 01:08:08"
}

Látható, hogy egy eléggé egyszerű adatszerkezet. A JSON leírása részletesen az alábbi címen érhető el: https://hu.wikipedia.org/wiki/JSON.

És akkor nézzük meg a konkrét implementációt. Először is bővítenünk kell a models/Reservations_model.php modellünket három metódussal.


        public function get_reservations() {
                $this->db->select()->from('reservations')->order_by('created_at', 'DESC');
                $query = $this->db->get();
                return $query->result();
        }

        public function get_reservation($id) {
                $query = $this->db->get_where('reservations', array('id' => $id));
                return $query->result();
        }

        public function approve_reservation($id) {
                $date = date('Y-m-d H:i:s');
                $this->db->set('updated_at', $date);
                $this->db->set('approved', 1);
                $this->db->where('id', $id);
                $this->db->update('reservations');
        }

Az első get_reservations() metódus a foglalások keletkezésének idejével csökkenő sorrendben egy listát ad vissza a foglalások adataival.

A második get_reservation($id) metódus egy konkrét foglalás adatait adja vissza.

A harmadik approve_reservation($id) metódus pedig UPDATE-eli, tehát frissíti a megadott ID-jű foglalás rekordunkat a frissítés dátumával és beállítja az jóváhagyott foglalás értékét 1-esre, ami esetünkben a jóváhagyás tényét jelzi adatbázis szinten.

Nézzük a views/admin.php view-nkat, hogy mi is kerül bele.

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li><a href="booking_ajax"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_booking'); ?></h2>
<table border="1">
<tr>
<th>Name</th><th>Email</th><th>Message</th><th>Checkin</th><th>Checkout</th><th>Created at</th><th>Updated at</th><th>Locale</th><th>Approved</th><th></th>
</tr>
<?php foreach ($query as $item):?>
<tr>
<form>
<td><?php echo $item->name;?></td>
<td><?php echo $item->email;?></td>
<td><?php echo $item->message;?></td>
<td><?php echo $item->checkin;?></td>
<td><?php echo $item->checkout;?></td>
<td><?php echo $item->created_at;?></td>
<td id="updated<?php echo $item->id;?>"><?php echo $item->updated_at;?></td>
<td><?php echo $item->locale;?></td>
<td id="approved<?php echo $item->id;?>"><?php echo $item->approved==1?'true':'false';?></td>
<td><input id="approve_button<?php echo $item->id;?>" type="button" value="Approve" class="btn btn-primary btn-modify" <?php echo $item->approved==1?'disabled':'';?>></td>
</form>
<script>
    $(document).ready(function(){
        $( "#approve_button<?php echo $item->id;?>" ).click(function(event)
        {
            $.ajax(
                {
                    type:"post",
                    url: "<?php echo $my_base_url; ?>index.php/apartments/admin_approve_ajax/<?php echo $item->id;?>",
                    data:{ },
                    contentType: "application/json",
                    dataType:"json", 
                    success:function(response)
                    {
                        console.log(response);
                        var obj = $.parseJSON(JSON.stringify(response));
                        $("#updated<?php echo $item->id;?>").html(obj.updated_at);
                        $('#approved<?php echo $item->id;?>').html(obj.approved==1?'true':'false');
                        $('#approve_button<?php echo $item->id;?>').prop('disabled', obj.approved==1?true:false);
                    },
                    error: function(response)
                    {
                        console.log(response);
                        alert("Invalide id=<?php echo $item->id;?>!");
                    }
                }
            );
        });
    });
</script>
</tr>
<?php endforeach;?>
</table>
                                </div>
                        </div>
                </div>
        </div><!-- END container -->

Látható, hogy ebbe egy táblázat került a foglalások adataival, melynek minden sora egy-egy foglalást tartalmaz. Erről az iterációról a <?php foreach ($query as $item):?><?php endforeach;?> blokk gondoskodik. Minden sort a foglalás ID-je azonosít. Minden sorhoz kerül egy-egy JavaScript blokk is a megfelelő Approve (Jóváhagy) gomb megfelelő “clickeseményének lekezelésével.

Felhívnám a figyelmet a JSON üzenet dekódolására. Ez a var obj = $.parseJSON(JSON.stringify(response)); sorban történik. Először szükséges sztringgé alakítani a válaszüzenetet, mivel az objektum-ként érkezik. Ez a JSON.stringify metódussal történik. Utána csak egy sima parse-olásra lesz szükségünk. 🙂

Nézzük meg végül a controllers/Apartments.php controller-ünket. Két új metódussal bővül. Az admin() metódusban csak kirajzoljuk az oldalunkat a modell réteg lista SQL SELECT metódusának meghívásával. Az admin_approve_ajax($id) metódusban pedig SQL UPDATE hívás majd egy SQL SELECT hívás történik a modell rétegben, illetve a válasz üzenet JSON becsomagolása.

        public function admin() {
                
                $this->load->model('reservations_model');

                $data['query'] = $this->reservations_model->get_reservations();

                $this->template->set('title', 'Admin');
                $this->template->load('menu_layout', 'contents' , 'admin', $data);
        }

        public function admin_approve_ajax($id) {

                $this->load->model('reservations_model');

                $this->reservations_model->approve_reservation($id);
                $data = $this->reservations_model->get_reservation($id)[0];

                header('Content-Type: application/json');
                echo json_encode($data);
        }

Látható, hogy viszonylag egyszerűen megoldottuk az egész adminisztrátori funkcionalitást az AJAX form vezérlés / adatátvitel és a JSON adatformátum hsználatával. 🙂

És akkor lássuk az admin oldalt (http://localhost/apartman/index.php/apartments/admin), egy jóváhagyott foglalással!

Látható, hogy a legfelső, az Approved true, tehát jóváhagyott rendelés – mivel csak angol nyelven implementáltam mindent az egyszerűség kedvéért – melletti Approve gomb már kiszürkült, nem megnyomható újra. Az Updated at – frissítés dátuma – pedig a késői vagy kora hajnali órát jelzi, amikor elkészültem a pilot kódokkal. 🙂

jQuery UI datepicker – a checkin és checkout mezők ellátása dátumválasztóval

Szeretnénk elérni, hogy a dátum mezőket ki lehessen választani a népszerű módon úgy, hogy “feljön” egy kis naptár egy ún. popup ablakban, ahol rákattintunk a kívánt napra, és a kivlasztott dátum bemásolódik a beviteli mezőnkbe a megadott formátumban.

Ehhez le kell tölenünk a jQuery UI-t a https://jqueryui.com/download/all/ webcímről, majd az assets könyvtárunk megfelelő js, css és css/image könyvtáraiba kell bemásolnunk a megfelelő file-okat.

A jQuery UI ún. datepicker – dátumválasztó widget-ét fogjuk használni. Leírása itt https://jqueryui.com/datepicker/.

A views/layouts/menu_layout.php sablonunkba a fejlécben (<head>) fell kell vegyük a jquery-ui.css illetve jquery-ui.js file-okat.

        <!-- jquery ui -->
        <link rel="stylesheet" href="<?php echo $my_base_url?>assets/css/jquery-ui.css">

        <!-- jquery ui -->
        <script src="<?php echo $my_base_url?>assets/js/jquery-ui.js"></script>

Ezek után a views/booking_ajax.php view-nkban a <script> részhez az alábbi két új függvényt fűzzük hozzá a $(document).ready(function() rész alá új elemként.

        $( "#checkin" ).datepicker({ dateFormat: "yy-mm-dd" });
        $( "#checkout" ).datepicker({ dateFormat: "yy-mm-dd" });

Itt egy sima inicializálásról van szó ID Selector alapján, a megfelelő ISO 8601 formátumban. Már működik is! 🙂

Lehetne hozzá sőt kell is még írni egy belső validációt, ami mégegyszer ellenőrzi a dátumformátumot adatbázisba írás előtt, de most ettől eltekintunk.

Ugyanígy kimaradtak az form serialize aspektusai is az előző fejezet AJAX űrlapküldésénél, továbbá még két apróság. Ugyanis a kettővel ezelőtti fejezetben nem másoljuk be a mező értékeket abban az esetben a form-unkba, ha valami validációs hiba történik, illetve az előző fejezetben nem töröljük a form mezőket (reset), ha sikeres a foglalás.

Apróságnak tűnnek, de ezek különböztetnek meg egy professzionális weblapot egy kezdetlegesebbtől. 🙂

Egy egyszerű form elküldése, a HTTP POST metódus, DB INSERT, foglalás oldal

Bár a dolgok “belseje”, mégis nézzük csak meg, hogy a model rétegnek mit kell tudnia. A models/Reservations_model.php file tartalmazza az adatbázis beszúrást, azt a bizonyos DB INSERT-et a címből.

<?php
class Reservations_model extends CI_Model {

        public function insert_reservation() {    
                $date = date('Y-m-d H:i:s');
                $data = array(
                        'name' => $this->input->post('name'),
                        'email' => $this->input->post('email'),
                        'phone' => $this->input->post('phone'),
                        'message' => $this->input->post('message'),
                        'checkin' => $this->input->post('checkin'),
                        'checkout' => $this->input->post('checkout'),
                        'apartment_id' => 1,
                        'created_at' => $date,
                        'updated_at' => $date,
                        'locale' => $this->session->userdata('site_lang')
                );

                return $this->db->insert('reservations', $data);
        }

}

Az insert során vesszük a HTTP POST asszociatív tömbből a megfelelő értékeket, néhányat pedig “generálunk”, illetve “kiszámítunk”. Ilyen az apartment_id, a created_at, az updated_at és a locale (nyelv).

Ezt a model metódust a controller-ből fogjuk hívni. Picit megbonyolítottam ezt a controller-t, ahogy az látszani fog. Tettem bele egy viszonyag egyszerű validációt. A validáció vagyis ellenőrzés arra szolgál, hogy a formban – ahogy az példánkban látható – ne lehessen a név, email, foglalás tól-ig adatok megadása nélkül elküldeni a foglalást. 🙂

Nézzük a controllers/Apartments.php vezérlőnket! Kiegészítjük a booking() és a book() metódusokkal.

        public function booking()
        {
                $data = array();
                $data['validation_errors'] = '';
                if (!empty($this->session->flashdata('validation_errors'))) {
                        $data['validation_errors'] = $this->session->flashdata('validation_errors');
                }

                $this->template->set('title', $this->lang->line('menu_booking'));
                $this->template->load('menu_layout', 'contents', 'booking', $data);
        }

        public function book()
        {
                $this->load->library('form_validation');
                $this->form_validation->set_rules('name', 'Name', 'required');
                $this->form_validation->set_rules('email', 'Email', 'required');
                $this->form_validation->set_rules('checkin', 'Check-in', 'required');
                $this->form_validation->set_rules('checkout', 'Check-out', 'required');

                if ($this->form_validation->run() === FALSE) {
                        $this->session->set_flashdata('validation_errors', validation_errors());
                        redirect($_SERVER['HTTP_REFERER']);
                } else {
                        $this->load->model('reservations_model');
                        $this->reservations_model->insert_reservation();

                        $data = array();

                        $this->template->set('title', $this->lang->line('menu_booking'));
                        $this->template->load('menu_layout', 'contents', 'booking_accepted', $data);
                }
        }

Látható, hogy a book() metódusban hívjuk a modellünket, majd a booking_accepted oldalra irányítjuk a klienst. 🙂

Végül nézzük a két view-nkat. Előbb vegyük a formunkat tartalmazó views/booking.php állományt.

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li class="active"><a href="booking"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_booking'); ?></h2>
                                </div>
                        </div>
                </div>
                <div id="fh5co-contact">
                        <div class="row">
                                <div class="col-md-4 animate-box">
                                        <h3>Contact Information</h3>
                                        <ul class="contact-info">
                                                <li><i class="icon-location4"></i>1117 Budapest, Irinyi J. utca 42.</li>
                                                <li><i class="icon-phone3"></i>+ 1235 2355 98</li>
                                                <li><i class="icon-location3"></i><a href="#">info@yoursite.com</a></li>
                                                <li><i class="icon-globe2"></i><a href="#">www.yoursite.com</a></li>
                                        </ul>
                                </div>
                                <div class="col-md-8 animate-box">
                                        <div class="form-wrap">
                                                <div class="row">
                                                        <form action="book" method="POST">
                                                        <?php if ($validation_errors) { ?>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <h3><font color="red"><?php echo $validation_errors; ?></font></h3>
                                                                </div>
                                                        </div>
                                                        <?php } ?>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="name" class="form-control" placeholder="Name">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="email" class="form-control" placeholder="Email">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="phone" class="form-control" placeholder="Phone">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <textarea name="message" class="form-control" id="" cols="30" rows="15" placeholder="Message"></textarea>
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="checkin" class="form-control" placeholder="Check-in">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="text" name="checkout" class="form-control" placeholder="Check-out">
                                                                </div>
                                                        </div>
                                                        <div class="col-md-12">
                                                                <div class="form-group">
                                                                        <input type="submit" value="Book" class="btn btn-primary btn-modify">
                                                                </div>
                                                        </div>
                                                        </form>
                                                </div>
                                        </div>
                                </div>
                        </div>
                </div>
        </div><!-- END container -->

Ebben a fontos talán a $validation_errors változó kiíratása, illetve maga a form. Azt, hogy mi az action-je, és mi a metódus, az alábbi sor írja le.

<form action="book" method="POST">

Érdemes még megnézni a form egy mezőjének definiálását is.

<input type="text" name="name" class="form-control" placeholder="Name">

Ez annyit jelent, hogy szöveg beviteli mező, name névvel, ahol a vakszöveg a “Name” felirat lesz.

Fontos továbbá az elküldő gomb megadása is, més a form tag-en belül. A tag amúgy, HTML tag, azaz egy két kacsacsőr közé rejtett literál (sztring), amiben nincs további kacsacsőr karakter. Példa nyitó és záró tag-ekre: <form></form>. 🙂

<input type="submit" value="Book" class="btn btn-primary btn-modify">

Ez egy submit button – elküldő gomb, “Book” felirattal.

Ezt a formot a submit gomb megnyomása után a book() metódus fogja “megkapni” a controller-ben egy HTTP POST metódushívás után. A form mezőit egy a name attribútumokban megadott nevekkel kulcsolt asszociatív tömb fogja tartalmazni. Ennyi az egész! 🙂

Látható az adatbázisunkban, hogy a megfelelő rekord bekerült a reservations táblába (SQL konzol hozzáférés mysql -u apartman -p apartman_devel parancs kiadásával).

mysql> select * from reservations;
+----+-----------------+------------+------------+------------+------------+-----------------------+----------+--------------+---------+---------------------+---------------------+
| id | name            | email      | phone      | checkin    | checkout   | message               | approved | apartment_id | locale  | created_at          | updated_at          |
+----+-----------------+------------+------------+------------+------------+-----------------------+----------+--------------+---------+---------------------+---------------------+
|  5 | Gergely Vámosi  | mail@to.me | 0123456789 | 2020-07-29 | 2020-08-01 | Szeretnék egy szobát! |     NULL |            1 | english | 2020-07-29 01:33:20 | 2020-07-29 01:33:20 |
+----+-----------------+------------+------------+------------+------------+-----------------------+----------+--------------+---------+---------------------+---------------------+
1 row in set (0.00 sec)

Nézzük meg még a views/booking_accepted.php oldalt. Ez arról ad tájékoztatást az apartmant lefoglalni szándékozó ügyfélnek, hogy sikeres a foglalása!

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li class="active"><a href="booking"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_booking'); ?></h2>
                                </div>
                        </div>
                </div>
                <div id="fh5co-contact">
                        <div class="row">
                                <div class="col-md-4 animate-box">
                                        <h3>Thanks for booking!</h3>
                                </div>
                        </div>
                </div>
        </div><!-- END container -->

Vegyük kicsit górcső alá a biztonságot! Hisz adatot közlünk a szerverrel, bármit beírhatunk a weblapon az űrlapba, amit pár szűrőn áteresztve adatbázisban tárolunk!

Először is érdemes megvizsgálni azt, hogy mit írhatunk be a mezőkbe: speciális karakterek, többek között a HTML formátum speciális jelei, a <>” és ‘ jelek. Aztán az ún. SQL injection veszélyei, amikor is az adatbáziselérésen keresztül speciális, például rosszakaratú SQL parancsokat tudunk kiadni, ami akár lehet egy ;DELETE FROM <TÁBLANÉV> pontosvessző után beszúrt parancs is akár. Szerencsés esetben ezeknek az eseteknek a kiszűrését a keretrendszerünk biztosítja. 🙂

Érdemes továbbá megvizsgálni az esetleges ún. CSRF – Cross-site request forgery veszélyét is. Bővebben a https://hu.wikipedia.org/wiki/Cross-site_request_forgery cím alatt olvashatunk róla. Röviden arról van szó, hogy a böngészőn keresztül nem kívánt parancsok hajthatók végre egy weblapon a felhasználó tudta nélkül. Ebbe a témakörbe tartozik egyébként még az XSS – Cross-site scripting is. A CSRF ellen egyébként többnyire egy minden formhoz (űrlapohoz) egyedileg generált azonosító, egy speciális kód véd, ami üzenetváltásonként újragenerált (töken). Ez véd többek között egy form újraküldése ellen, azaz adott esetben egy művelet (pl. fizetés) adott esetben rossz szándékú újbóli végrehajtása ellen. Az XSS csupán egy weblap funkcióinak más weblapbóli történő meghívása. 🙂 Játék. A baj, ha nem barátágos.

Aztán ott van az úgynevezett Captcha-teszt szükségessége is. Bővebben a https://hu.wikipedia.org/wiki/Captcha címen olvashatunk róla. Itt arról van szó, hogy weblapunk az emberi felhasználót megkülönböztesse egy generált robot, azaz gépi hozzáféréstől. Csak valós emberektől szeretnénk apartman foglalást elfogadni! Semmi esetre sem rossz szándékú hekkerek által készített programoktól. Ismerősök ugye az elhanyagolt facebook profilok mindenféle “fake” felhasználó általi “hello, ismerjük egymást” üzenetei? Főleg attraktív “csajoktól”? 🙂

És akkor lássuk a http://localhost/apartman/index.php/apartments/booking oldalt!

Egyszerű oldal létrehozása – képek oldal

Egy egyszerű oldalt az apartmanunkat bemutató képekkel három egyszerű lépésben hozhatunk létre. A demo képeket letöltöttük ingyenesen a https://www.pexels.com/ oldalról és előzőleg már előkészítettük.

Először is a controller-ünket kell megbűvölni. A controllers/Apartments.php file-hoz az alábbi sorokat fűzzük hozzá.

        public function photos()
        {
                $data = array();

                $this->template->set('title', $this->lang->line('menu_photos'));
                $this->template->load('menu_layout', 'contents', 'photos', $data);
        }

Másodjára létrehozzuk a views/layouts/menu_layout.php sablon-t, ami alapvetően annyival tud többet a default_layout.php file-unknál, hogy a menüt is meg lehet benne változtatni. Erre csupán azért van szükségünk, hogy az aktuális menüpont piros lehessen. 🙂

<?php
        $my_base_url = '/apartman/';
?>
<!DOCTYPE HTML>
<html>
        <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Apartman - <?php echo $title;?></title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="Apartman booking website" />
        <meta name="keywords" content="apartman, booking, website" />
        <meta name="author" content="Gergely Vámosi" />

        <!-- 
        //////////////////////////////////////////////////////

        FREE HTML5 TEMPLATE 
        DESIGNED & DEVELOPED by FreeHTML5.co
                
        Website:                http://freehtml5.co/
        Email:                  info@freehtml5.co
        Twitter:                http://twitter.com/fh5co
        Facebook:               https://www.facebook.com/fh5co

        //////////////////////////////////////////////////////
         -->

        <!-- Facebook and Twitter integration -->
        <meta property="og:title" content=""/>
        <meta property="og:image" content=""/>
        <meta property="og:url" content=""/>
        <meta property="og:site_name" content=""/>
        <meta property="og:description" content=""/>
        <meta name="twitter:title" content="" />
        <meta name="twitter:image" content="" />
        <meta name="twitter:url" content="" />
        <meta name="twitter:card" content="" />

        <link href="https://fonts.googleapis.com/css?family=Open+Sans|Playfair+Display" rel="stylesheet">
        
        <!-- Animate.css -->
        <link rel="stylesheet" href="<?php echo $my_base_url?>assets/css/animate.css">
        <!-- Icomoon Icon Fonts-->
        <link rel="stylesheet" href="<?php echo $my_base_url?>assets/css/icomoon.css">
        <!-- Bootstrap  -->
        <link rel="stylesheet" href="<?php echo $my_base_url?>assets/css/bootstrap.css">

        <!-- Flexslider  -->
        <link rel="stylesheet" href="<?php echo $my_base_url?>assets/css/flexslider.css">

        <!-- Theme style  -->
        <link rel="stylesheet" href="<?php echo $my_base_url?>assets/css/style.css">

        <!-- Modernizr JS -->
<script src="<?php echo $my_base_url?>assets/js/modernizr-2.6.2.min.js"></script>
        <!-- FOR IE9 below -->
        <!--[if lt IE 9]>
<script src="<?php echo $my_base_url?>assets/js/respond.min.js"></script>
        <![endif]-->

        </head>
        <body>
                
        <div class="fh5co-loader"></div>
        
        <div id="page">
        <nav class="fh5co-nav" role="navigation">
                <div class="container">
                        <div class="top-menu">
                                <div class="row">
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a style="font-size: 14px; padding: 5px 5px;" href="<?php echo $my_base_url?>index.php/LanguageSwitcher/switchLang/magyar">Magyar</a></li>
                                                        <li><a style="font-size: 14px; padding: 5px 5px;" href="<?php echo $my_base_url?>index.php/LanguageSwitcher/switchLang/english">English</a></li>
                                                        <li><a style="font-size: 14px; padding: 5px 5px;" href="<?php echo $my_base_url?>index.php/LanguageSwitcher/switchLang/deutsch">Deutsch</a></li>
                                                </ul>
                                        </div>
                                </div>
                                <div class="row">
                                        &nbsp;
                                </div>

                <!-- PAGE CONTENT BEGINS -->
                        <?php echo $contents;?>
                <!-- PAGE CONTENT ENDS -->

        <div class="container">
                <footer id="fh5co-footer" role="contentinfo">
                        <div class="row">
                                <div class="col-md-3 fh5co-widget">
                                        <h4>Az oldalról</h4>
                                        <p>Jogi információ.</p>
                                </div>
                                <div class="col-md-3 col-md-push-1">
                                        <h4>Lorem Ipsum</h4>
                                        <ul class="fh5co-footer-links">
                                                <li><a href="#">12345</a></li>
                                                <li><a href="#">67890</a></li>
                                        </ul>
                                </div>

                                <div class="col-md-3 col-md-push-1">
                                        <h4>Linkek</h4>
                                        <ul class="fh5co-footer-links">
                                                <li><a href="#">Home</a></li>
                                                <li><a href="#">Képek</a></li>
                                                <li><a href="#">Foglalás</a></li>
                                                <li><a href="#">Kapcsolat</a></li>
                                        </ul>
                                </div>

                                <div class="col-md-3">
                                        <h4>Kapcsolat</h4>
                                        <ul class="fh5co-footer-links">
                                                <li>Kossuth utca 10,<br>1111 Budapest</li>
                                                <li><a href="tel://1234567920">+ 1235 2355 98</a></li>
                                                <li><a href="mailto:info@yoursite.com">info@yoursite.com</a></li>
                                                <li><a href="http://localhost/apartman">apartman</a></li>
                                        </ul>
                                </div>

                        </div>

                        <div class="row copyright">
                                <div class="col-md-12 text-center">
                                        <p>
                                                <small class="block">&copy; 2016 Free HTML5. All Rights Reserved.</small>
                                                <small class="block">Designed by <a href="http://freehtml5.co/" target="_blank">FreeHTML5.co</a> Demo Images: <a href="http://unsplash.co/" target="_blank">Unsplash</a></small>
                                        </p>
                                        <p>
                                                <ul class="fh5co-social-icons">
                                                        <li><a href="#"><i class="icon-twitter"></i></a></li>
                                                        <li><a href="#"><i class="icon-facebook"></i></a></li>
                                                        <li><a href="#"><i class="icon-linkedin"></i></a></li>
                                                        <li><a href="#"><i class="icon-dribbble"></i></a></li>
                                                </ul>
                                        </p>
                                </div>
                        </div>
                </footer>
        </div><!-- END container -->
        </div>

        <div class="gototop js-top">
                <a href="#" class="js-gotop"><i class="icon-arrow-up2"></i></a>
        </div>
        
        <!-- jQuery -->
<script src="<?php echo $my_base_url?>assets/js/jquery.min.js"></script>
        <!-- jQuery Easing -->
<script src="<?php echo $my_base_url?>assets/js/jquery.easing.1.3.js"></script>
        <!-- Bootstrap -->
<script src="<?php echo $my_base_url?>assets/js/bootstrap.min.js"></script>
        <!-- Waypoints -->
<script src="<?php echo $my_base_url?>assets/js/jquery.waypoints.min.js"></script>
        <!-- Flexslider -->
<script src="<?php echo $my_base_url?>assets/js/jquery.flexslider-min.js"></script>
        <!-- Sticky Kit -->
<script src="<?php echo $my_base_url?>assets/js/sticky-kit.min.js"></script>
        <!-- Main -->
<script src="<?php echo $my_base_url?>assets/js/main.js"></script>

        </body>
</html>

Harmadjára pedig hozzuk létre a sablonban a $contents helyére kerülő views/photos.php file-t.

<?php
        $my_base_url = '/apartman/';
?>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li class="active"><a href="photos"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li><a href="booking"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li><a href="contact"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>
        <div class="container">
                <div id="fh5co-intro">
                        <div class="row animate-box">
                                <div class="col-md-8 col-md-offset-2 col-md-pull-2">
                                        <h2><?php echo $this->lang->line('menu_photos'); ?></h2>
                                </div>
                        </div>
                </div>
                <div id="fh5co-portfolio">
                        <div class="row">

                                <div class="col-md-4 col-md-push-8 sticky-parent">
                                        <div class="detail" id="sticky_item">
                                                <div class="animate-box">
                                                        <h2>Lorem ipsum</h2>
                                                        <span>Lorem ipsum dolor sit amet</span>
                                                        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
                                                        <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
                                                        <p><a class="btn btn-primary btn-demo" href="#"></i> Lorem ipsum</a></p>
                                                </div>
                                        </div>
                                </div>

                                <div class="col-md-7 col-md-pull-4 image-content">
                                        <div class="image-item  animate-box">
                                                <img src="<?php echo $my_base_url?>assets/images/kitchen_480_.jpg" class="img-responsive" alt="Photo by Dmitry Zvolskiy from Pexels">
                                        </div>
                                        <div class="image-item  animate-box">
                                                <img src="<?php echo $my_base_url?>assets/images/interior_480.jpg" class="img-responsive" alt="Photo by Daria Shevtsova from Pexels">
                                        </div>
                                        <div class="image-item  animate-box">
                                                <img src="<?php echo $my_base_url?>assets/images/livingroom_480.jpg" class="img-responsive" alt="Photo by Daria Shevtsova from Pexels">
                                        </div>
                                        <div class="image-item  animate-box">
                                                <img src="<?php echo $my_base_url?>assets/images/bedroom_480.jpg" class="img-responsive" alt="https://www.pexels.com/">
                                        </div>
                                        <div class="image-item  animate-box">
                                                <img src="<?php echo $my_base_url?>assets/images/bathroom_480.jpg" class="img-responsive" alt="Photo by Jean van der Meulen from Pexels">
                                        </div>
                                </div>

                        </div>
                </div>
        </div><!-- END container-wrap -->

Ennyi az egész! Máris láthatjuk a http://localhost/apartman/index.php/apartments/photos cím alatt a szépséges “Képek” oldalunkat!

Többnyelvűség implementálása: Magyar, English, Deutsch

Szeretnénk, ha az oldalunk több nyelven is tudna, mivel lehetnek az apartmannak külföldi vendégei is.

A CodeIgniter ezt alapból jól megtámogatja úgynevezett nyelvi file-okkal, illetve a hook funkcióval, nézzünk a megvalósításra egy egyszerű utat. Az alábbi webhelyen elég jó útmutatást kapunk, csupán egy picit változtatunk az implementáción.

http://programmerblog.net/multilingual-website-in-codeigniter/

Először hozzunk létre nyelvi könyvtárakat. Nálunk az alapértelmezett nyelv a magyar lesz (angol, “english” ugye már alapból van).

gvamosi@gergo1:/var/www/html/apartman/application$ mkdir language/magyar
gvamosi@gergo1:/var/www/html/apartman/application$ mkdir language/deutsch

Az language/magyar/base_lang.php file tartalma a követező legyen.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

$lang['menu_photos'] = 'Képek';
$lang['menu_booking'] = 'Foglalás';
$lang['menu_contact'] = 'Kapcsolat';
?>

Erre a mintára készüljön az angol nyelvű nyelvi file is: language/english/base_lang.php.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

$lang['menu_photos'] = 'Photos';
$lang['menu_booking'] = 'Booking';
$lang['menu_contact'] = 'Contact';
?>

Majd a német is: language/deutsch/base_lang.php. 🙂

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

$lang['menu_photos'] = 'Fotos';
$lang['menu_booking'] = 'Reservieren';
$lang['menu_contact'] = 'Kontakt';
?>

A config/config.php-ban engedélyeznünk kell a hook funkciót.

$config['enable_hooks'] = TRUE;

Meg kell írnunk a hook-unkat a config/hooks.php file-ban.

$hook['post_controller_constructor'] = array(
	'class' => 'LanguageLoader',
	'function' => 'initialize',
	'filename' => 'LanguageLoader.php',
	'filepath' => 'hooks'
);

Az application könyvtár alatt létre kell hozzuk a hooks/LanguageLoader.php osztályt.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class LanguageLoader
{
   function initialize() {
       $ci =& get_instance();
       $ci->load->helper('language');
       $siteLang = $ci->session->userdata('site_lang');
       if ($siteLang) {
           $ci->lang->load('base', $siteLang);
       } else {
           $ci->lang->load('base', 'magyar');
       }
   }
}
?>

Ezután létre kell hoznunk egy controller-t történetesen controllers/LanguageSwitcher.php néven.

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class LanguageSwitcher extends CI_Controller {
  public function __construct(){
    parent::__construct();
  }
  public function switchLang($language = "") {
    $this->session->set_userdata('site_lang', $language);
    redirect($_SERVER['HTTP_REFERER']);
  }
}

Némi magyarázatot fűznék a fenti két kódblokkhoz. A “session” magyar fordítáa munkamenet szokott lenni. Ha itt beállítunk egy változó értéket, akkor kliens oldalon a browser-ben egy weblaphoz kötött változót, egy ún. cookie-t, magyarul sütit tárol el a szerver alkalmazás ilyenkor. A szerver gép pedig az adott süti által meghatározott böngésző munkamenethez egy session változót regisztrál, esetünkben az oldal nyelvét, vagyis hogy magyar, angol vagy német nyelven kívánjuk olvasni az weblapot. Nagyon szépen meg lehet ezt nézni pl. a Chrome böngészőben a Developer Tools-ban az Application / Storage / Cookies rész alatt. Ott találunk egy localhost-tól bejegyzett sütit, ci_session néven, egy egyedi azonosítóval, egy ID-vel. A webszerverünkön a session, vagyis a HTTP SESSION egy idő után, ha nem történik újabb kommunikáció a weboldalon a szerver és a kliens között, elévül és törlődik. Milyen elmés! 🙂

Magyarázzuk meg még a “HTTP_REFERER” HTTP header, magyarul fejléc változó használatát. A HTTP kérés és válasz tartalmaz egy fejlécet és egy törzset (body), mint minden normális IP üzenet. Ez a fejléc érték azt az url-t tartalmazza, ahonnan az adott url-t “hívtuk”. Ebben a megoldásban, mivel bármely oldalon állíthatjuk a nyelvet, a hívó oldalra visszairányítjuk automatikusan a klienst, miután a /LanguageSwitcher/switchLang/<NYELV> url segítségével a megfelelő controller-ben átállítottuk a nyelvet a session-ben. 🙂

Bővebben az előbbiekről a https://hu.wikipedia.org/wiki/HTTP-s%C3%BCti és a https://hu.wikipedia.org/wiki/Referer címeken olvashatunk.

Ezután nem marad más hátra, mint “bedrótozni” egy menüstruktúrát a sablonfile-unkban, illetve a $this->lang->line('message') hívással előszedni a megfelelő sztringeket a nyelvi file-okból. Nézzük az új navigációt.

        <nav class="fh5co-nav" role="navigation">
                <div class="container">
                        <div class="top-menu">
                                <div class="row">
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li><a style="font-size: 14px; padding: 5px 5px;" href="<?php echo $my_base_url?>index.php/LanguageSwitcher/switchLang/magyar">Magyar</a></li>
                                                        <li><a style="font-size: 14px; padding: 5px 5px;" href="<?php echo $my_base_url?>index.php/LanguageSwitcher/switchLang/english">English</a></li>
                                                        <li><a style="font-size: 14px; padding: 5px 5px;" href="<?php echo $my_base_url?>index.php/LanguageSwitcher/switchLang/deutsch">Deutsch</a></li>
                                                </ul>
                                        </div>
                                </div>
                                <div class="row">
                                        &nbsp;
                                </div>
                                <div class="row">
                                        <div class="col-sm-2">
                                                <div id="fh5co-logo"><a href="index.html">Apartman<span>.</span></a></div>
                                        </div>
                                        <div class="col-sm-10 text-right menu-1">
                                                <ul>
                                                        <li class="active"><a href="index.html"><?php echo $this->lang->line('menu_photos'); ?></a></li>
                                                        <li><a href="single.html"><?php echo $this->lang->line('menu_booking'); ?></a></li>
                                                        <li><a href="contact.html"><?php echo $this->lang->line('menu_contact'); ?></a></li>
                                                </ul>
                                        </div>
                                </div>
                                
                        </div>
                </div>
        </nav>

Kész a többnyelvű oldalunk! 🙂

CodeIgniter modell réteg az adatbázissal és a Hello, World!

Programozóknak ugyan nem kell bemutatni Hello, World! fogalmát, de most kell hogy beszéljünk róla. A mai fejezetben szeretnénk létrehozni az adatbázist MySQL-ben, legyártani a modell réteget, majd adattal feltölteni, és a weben megjelentíteni. Mindezt mind saját kézzel írt kóddal, lehetőleg kevés kódsorban megvalósítva! Ez a Hello, World! – Helló, Világ! példánkban.

https://en.wikipedia.org/wiki/%22Hello,_World!%22_program

Hozzunk létre egy adatbázist a MySQL szerverünkön a https://wlammpp.wordpress.com/2019/10/04/i-resz-wordpress-webfejlesztes-a-wordpress-telepitese korábbi fejezetben ismertetett módon. Van egy adatbázis dump-om, ezt kell “bejátszani”.

-- MySQL dump 10.13 Distrib 5.7.29, for Linux (x86_64)
-- Host: localhost Database: evaap_devel

-- Server version 5.7.29
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table apartments
DROP TABLE IF EXISTS apartments;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE apartments (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(255) DEFAULT NULL,
address varchar(255) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table apartments
LOCK TABLES apartments WRITE;
/*!40000 ALTER TABLE apartments DISABLE KEYS */;
INSERT INTO apartments VALUES (1,'Budapest I','VII. kerulet','2020-03-20 00:26:35','2020-03-20 00:26:35');
/*!40000 ALTER TABLE apartments ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table reservations
DROP TABLE IF EXISTS reservations;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE reservations (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(255) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
phone varchar(255) DEFAULT NULL,
checkin date DEFAULT NULL,
checkout date DEFAULT NULL,
message text,
approved tinyint(1) DEFAULT NULL,
apartment_id int(11) DEFAULT NULL,
locale varchar(255) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table users
DROP TABLE IF EXISTS users;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE users (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(255) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
password varchar(255) DEFAULT NULL,
is_admin tinyint(1) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table users
LOCK TABLES users WRITE;
/*!40000 ALTER TABLE users DISABLE KEYS */;
INSERT INTO users VALUES (1,'admin','gvamosi@linux.hu','manager',1,'2020-03-19 00:00:00','2020-03-19 00:00:00');
/*!40000 ALTER TABLE users ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2020-04-07 14:05:14

Az alábbi módon:

gvamosi@gergo1:/var/www/html/apartman$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 50
Server version: 5.7.29 MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> CREATE DATABASE apartman_devel CHARACTER SET utf8 COLLATE utf8_general_ci;
Query OK, 1 row affected (0.01 sec)
mysql> CREATE USER 'apartman'@'localhost' IDENTIFIED BY 'apartman1';
Query OK, 0 rows affected (0.02 sec)
mysql> GRANT ALL PRIVILEGES ON apartman_devel.* TO 'apartman'@'localhost';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)
mysql> \q
Bye
gvamosi@gergo1:/var/www/html/apartman$ mysql -u apartman -p apartman_devel < db/create_and_insert_tables.sql
Enter password:
gvamosi@gergo1:/var/www/html/apartman$

Hozzuk létre az alkalmazás modell rétegét! Ehhez először be kell konfigurálnunk az adatbázist.

gvamosi@gergo1:/var/www/html/apartman/application$ vi config/database.php

$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'apartman',
'password' => 'apartman1',
'database' => 'apartman_devel',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);

Ez eddig 3 string (karakterlánc)! 🙂 Aztán meg kell írnunk az Apartments_model.php file-t!

gvamosi@gergo1:/var/www/html/apartman/application$ vi models/Apartments_model.php

<?php
class Apartments_model extends CI_Model {
public function get_last_ten_entries()
{
$query = $this->db->get('apartments', 10);
return $query->result();
}
}

Ez 8 sor. 🙂 Most írjuk meg a controller-t!

gvamosi@gergo1:/var/www/html/apartman/application$ vi controllers/Apartments_controller.php
<?php
class Apartments_controller extends CI_Controller {
public function index() {
echo 'Helló, Világ!!';
$this->load->model('apartments_model');
$data['query'] = $this->apartments_model->get_last_ten_entries();
$this->load->view('apartments', $data);
}
}

Ez újabb 9 sor! Nem sok ez egy listáért? És akkor írjuk meg a view-t is!

gvamosi@gergo1:/var/www/html/apartman/application/views$ vi apartments.php

<ul>
<?php foreach ($query as $item):?>
<li><?php echo $item->address;?></li>
<?php endforeach;?>
</ul>

Ez húzós 5 sor! 🙂 Állítsuk be az útvonalakat, az autoload-ot (még két sor :)), majd hívjuk meg a következő módon a böngészőnkből az “oldalunkat”!

gvamosi@gergo1:/var/www/html/apartman/application$ vi config/routes.php
$route['apartmanok'] = 'apartments_controller/index';

gvamosi@gergo1:/var/www/html/apartman/application$ vi config/autoload.php
$autoload['libraries'] = array('database','session');

http://localhost/apartman/index.php/apartmanok

Listázza az egy darab apartmanunk címét, Budapesten. Siker! 🙂

II. rész – Airbnb apartman foglalás – esettenulmány – PHP CodeIgniter keretrendszerben

Amint megígértem, ebben az évben folytatom, és remélhetőleg be is fejezem a webprogramozásról szóló online könyvemet. 🙂

Hogy jobban megértsük a webprogramozást, egy kis architektúrális ismertetőt kell tartsak. Általánosságban már szóltam a kliens-szerver felépítésről (https://wlammpp.wordpress.com/2019/10/03/mit-ertunk-webprogramozas-webfejlesztes-alatt). Egy webes alkalmazás a felhasználóval a böngészőn (browser) keresztül “lép interaktív kapcsolatba”. Ez azt jelenti, hogy a felhsználó egy böngésző ablakban illetve tabfülön keresztül látja illetve használja a grafikus felhasználói felületet (GUI – graphical user interface, ide tartozik még a népszerű Ui / UX Design fogalma, vagyis a felhasználói felület és élmény tervezés). A felhasználó az alkalmazás funkcióit a böngészőben megjelenő felületen keresztül éri el: elolvas információkat, regisztrál egy webes űrlapon (formon) keresztül, keres, navigál, “kattint” (click-el). Ezek közül komolyabb programozást az űrlapkitöltés és a keresés igényel, illetve a különböző grafikai látványelemek is.

Az egész webprogramozás illetve -fejlesztés azért tűnhet bonyolultnak, mert egyrészt kellenek a “szerver oldali” dolgok: egy adatbázis – esetünkben relációs -, amit az ún. SQL nyelven lehet manipuláni, lekérdezni, feltölteni, frissíteni illetve törölni (https://hu.wikipedia.org/wiki/SQL), továbbá egy beágyazott, illetve szerver oldalon értelmezett (ún. script) nyelv, ez a PHP 7-es verzió konkrét esetünkben (https://www.php.net/), amely a funkcionalitást, az “üzleti folyamatot” valósítja meg (implementálja). A PHP Hyprtext Preprocessor-nak hirdeti magát, ami azt jelenti, hogy a HTML nyelven (ez a weblapok nyelve) írt hypertext-be van beágyazva, amit a webszerverünk megfelelő modulja értelmez, amikor valaki az adott weboldalt a böngészőjében megnyitja. Tehát nincs előre lefordítva, mint például egy C nyelven írt kernel modul (Linux), hanem “on the fly” fordul (compile), és “fut” (run). Ezt szemléltetném is egy pár soros kódban.

<!DOCTYPE html>
<html>
<head>
<title>Lapcím</title>
</head>
<body>

<h1>Ez egy címsor.</h1>
<p>Ez egy bekezdés.</p

<!-- Ez egy megjegyzés. Ide jön a PHP kódrészlet. -->

<?php
phpinfo();
?>

</body>
</html>

Ebben a rövid kódrészletben (snippet) ún. HTML tag-eket láthatunk. Az eleje egy dokumentum típus megjelölés, majd html, head – fejléc, body – törzs olvashatóak. Látható, hogy <? ?> jelek között van a PHP kódrészlet, ami konkrétan egy hosszú és részletes kiírást eszközöl a szerverünk beállításáról, ha megnézzük böngészőben (írtam már róla itt: https://wlammpp.wordpress.com/2019/10/04/i-resz-wordpress-webfejlesztes-a-wordpress-telepitese). Ezek a tag-ek mind “kacsacsőrök” között vannak. Ez a leíró nyelvek sajátossága. Az XML is ilyen formátumú. 🙂

A HTML oldal önmagában statikus, ha pl. PHP kódrészletet nem tartalmaz. Lehet benne hivatkozás képre, hangra, videóra, tehát Hypertext, ugyanakkor nem igényel szerveroldali processzálást (feldolgozást). Ezért a szerveren statikus oldalként kerül kiszolgálásra. Erre általában az oldalt tartalmazó állomány nevének kiterjesztése utal, ez többnyire .html – PHP oldalak esetében pedig .php. A nagy különbség egy “honlap” és egy “webalkalmazás” között abban rejlik, hogy milyen és mennyi “kód” van mögötte és benne a szerveren, illetve a kliens oldalon JavaScript formájában, értsd: hogy mennyire interaktív. Alábbiakban egy kis összefoglaló a HTML struktúráról, a tag-ekről.

<!-- Nyitó és záró tag, közte a tartalom. -->
<tagnev>Töltelék.</tagnev>

<!-- Önmagában záródó tag (XML helyes), paraméterekkel. -->
<tagnev parameter1="ertek1" parameter2="ertek2" />

<!-- A legegyszerűbb "beágyazás". -->
<tag1>
<tag2 />
</tag1>

Ha ez már bonyolult volna, szólnom kell még a kliens oldalon fontossá váló komponensekről. Ezek az előbbiekben taglalt az oldalak alapvető struktúrájáért és megjelenéséért felelős HTML (Hypertext Markup Language – https://www.w3schools.com/html/), a látványért felelős CSS (https://hu.wikipedia.org/wiki/Cascading_Style_Sheets, https://www.w3schools.com/css/) illetve a dinamikus működésért felelős JavaScript (https://www.w3schools.com/js/) nyelveken implementált böngészőben futó, “héj” réteg. A könyv legelején taglalt MVC architektúra (Model – View – Controller) View – nézet rétegéről szól ez a kliens oldali rész. A dinamikus működést biztosító JavaScript, ha AJAX-ról (https://hu.wikipedia.org/wiki/Ajax_(programoz%C3%A1s)) beszélünk, akkor lehet “okos” Controller – vezérló működésű is, sőt bizonyos szempontból még Model – adatszerkezet érintettségről is beszélhetünk, amennyiben XML-formátumban történik az adatátvitel pl. egy kliens oldali űrlap (form) és a szerver oldali Controller között.

A szerveroldali részek azonban a Model és a Controller rétegbe tartoznak. Technikailag ugyan a View is a szerver oldalon van, ámde csupán a böngészőben válik látvánnyá. Ha jól implementáltuk (programoztuk) az alkalmazást, az architektúrális elveknek megfelelően, akkor elválnak egymástól a rétegek. Ettől áttekinthető és karbantartható marad a kódunk. Sokkal kisebb a hibalehetőség, illetve egyszerűbb továbbfejleszteni, módosítani és dokumentálni. Hogy ezt az architektúrális szétválasztást tényleg meg is tehessük, segítségül hívtuk a CodeIgniter (https://codeigniter.com/) keretrendszert (framework-öt).

A CodeIgniter-t (3-as verzió) egész egyszerűen letöltjük (https://codeigniter.com/download), és kicsomagoljuk a webszerverünk gyökér könyvtára alatt létrehozott “apartman” könyvtárba.

gvamosi@gergo1:/var/www/html$ sudo mkdir apartman
[sudo] password for gvamosi:
gvamosi@gergo1:/var/www/html$ sudo chown gvamosi:gvamosi apartman/
gvamosi@gergo1:/var/www/html$ cd apartman/
gvamosi@gergo1:/var/www/html/apartman$ cp ~/Downloads/bcit-ci-CodeIgniter-3.1.11-0-gb73eb19.zip .
gvamosi@gergo1:/var/www/html/apartman$ unzip bcit-ci-CodeIgniter-3.1.11-0-gb73eb19.zip
gvamosi@gergo1:/var/www/html/apartman$ mv bcit-ci-CodeIgniter-b73eb19/* .
gvamosi@gergo1:/var/www/html/apartman$ l
total 3836
drwxr-xr-x 14 gvamosi gvamosi 4096 Sep 19 2019 application
-rw-r--r-- 1 gvamosi gvamosi 2758337 Apr 6 11:30 bcit-ci-CodeIgniter-3.1.11-0-gb73eb19.zip
drwxr-xr-x 2 gvamosi gvamosi 4096 Apr 6 11:32 bcit-ci-CodeIgniter-b73eb19
-rw-r--r-- 1 gvamosi gvamosi 594 Sep 19 2019 composer.json
-rw-r--r-- 1 gvamosi gvamosi 6841 Sep 19 2019 contributing.md
-rw-r--r-- 1 gvamosi gvamosi 1117463 Apr 6 11:23 framework-4.0.2.zip
-rwxr-xr-x 1 gvamosi gvamosi 10255 Sep 19 2019 index.php
-rw-r--r-- 1 gvamosi gvamosi 1114 Sep 19 2019 license.txt
-rw-r--r-- 1 gvamosi gvamosi 2343 Sep 19 2019 readme.rst
drwxr-xr-x 8 gvamosi gvamosi 4096 Sep 19 2019 system
drwxr-xr-x 14 gvamosi gvamosi 4096 Sep 19 2019 user_guide

Ezek után megnézhetjük a “helyi” webszerverünket, hogy sikerült-e a telepítés. Írjuk be a kedvenc böngészőnkbe (én a Google Chrome-ot használom) a következő címet: http://localhost/apartman/. 🙂

Siker! 🙂 Jól látható, hogy vagy 5 nyelven kell tudnunk kódolni egy átlagos webalkalmazáshoz. 😀 Megjegyzem, hogy ez az esettanulmány ugyan konkrét példát mutat be, azonban általános érvényű az architektúrában és főbb irányelveiben.