Giter Site home page Giter Site logo

workspace's Introduction

Workspace

workspace's People

Contributors

khanhicetea avatar

Stargazers

Vo The Vinh avatar PHẠM NGỌC ĐIỆP avatar  avatar

Watchers

James Cloos avatar  avatar Vo The Vinh avatar  avatar  avatar

workspace's Issues

Sử dụng Captcha trong sifoni

Bài viết này mình sẽ nói về cách sử dụng captcha trong sifoni một cách đơn giản nhất mà mình đã được được cha đẻ của sifoni truyền lại ......kkkkk

Bước 1 : Tạo captcha

Tạo file Captcha.php nằm trong thư mục controller với nội dung sau

<?php
namespace App\Controller;

use Sifoni\Controller\Base;

class Captcha extends Base
{
    private function genRandomString($length = 6) {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
        $random = '';    

        for ($p = 0; $p < $length; $p++) {
            $random .= $characters[mt_rand(0, strlen($characters))];
        }

        return $random;
    }

    private function getNewCaptcha($length = 6, $fontSize = 6)
    {
        header("Content-type: image/png");

        $random = $this->genRandomString($length);
        $this->app['session']->set('captcha_hash', md5($random));
        $width = imagefontwidth($fontSize)* strlen($random) + 6;
        $height = imagefontheight($fontSize) + 6;
        $im  = imagecreatetruecolor($width, $height);
        $bgc = imagecolorallocate($im, 0, 0, 0);
        $tc  = imagecolorallocate($im, 255, 255, 255);
        imagefilledrectangle($im, 0, 0, $width, $height, $bgc);
        imagestring($im, $fontSize, 3, 3, $random, $tc);
        imagepng($im);
        exit();
    }

    public function renderAction() {
        $this->getNewCaptcha();
    }
}

Hàm genRandomString nhằm tạo ra một chuỗi kí tự bất kì,hàm getNewCaptcha giúp bạn tạo ra chuỗi captcha,lưu ý chúng ta sẽ sử dụng session để lưu lại giá trị của captcha nhằm thuận lợi cho việc check captcha sau này(thay vì phải viết thêm hàm check captcha chúng ta sẽ kiểm tra trực tiếp với session )
Hàm renderAction gọi 1 captcha mới,như vậy đã xong phần tạo captcha giờ tiến tới việc sử dụng

Bước 2 : Tạo routing cho captcha

Bạn thêm dòng này vào file routing.php

'/captcha/new' => 'Captcha:render:captcha_render',

Bước 3 : Sử dụng captcha

Giả sử bạn sử dụng captcha trong chức năng người dùng gửi phản hồi hay gửi bài về web,bạn có thể tạo ra 1 đoạn code nằm trong form như sau :

        <div class="form-group">
            <label for="text" class="col-sm-2 control-label">Captcha</label>
            <div class="col-sm-10">
                <img src="{{ url('captcha_render') }}" />
                <input type="text" value="" name="captcha"></input>
            </div>
        </div>

Với <img src="{{ url('captcha_render') }}" /> gọi ra captcha và <input type="text" value="" name="captcha"></input> để người dùng nhập giá trị vào

Bước 4 : Kiểm tra giá trị nhập vào với mã captcha

Tại `controller``

$frmData = $this->getPostData();
            $captcha = md5($frmData['captcha']);
            if ($captcha == $this->app['session']->get('captcha_hash','null')) {
                 }

Bạn tiến hành get giá trị từ form về bình thường sau đó kiểm giá trị đó với session chứa captcha đã set từ trước...
Chúc bạn thành công !

Xử lý Tag triệt để khi tạo tag cho bài viết

Vấn đề

Ở cuối bài viết hay có Tag hoặc các từ khóa. Làm thế nào để tạo và xử lý các tag cho nhẹ nhàng, thanh thoát và mỹ miều nhất là vấn đề được đề cập ở issue này.

Hướng xử lý

Bước 1: Tạo table trong database

Chúng ta cần phải tạo ra 3 table:
Articles (bài viết)
Title (varchar)
Content (text)
Tags (varchar)

Tags
ID (int)
Name (varchar)

Articles-Tags
article_id (int)
tag_id (int)

Bước 2: Lưu trữ và xử lý

Trong view để nhập bài viết ta tạo input:
<input type="text" name="tags">
Insert
Yêu cầu người sử dụng nhập các tags cách nhau bằng dấu phẩy.

  • Khi người dùng submit, lưu thông tin vào table article bình thường.
  • Sau khi lưu xong, lấy ID của bài viết vừa tạo lưu vào biến $idArticle, để dành xử lý ở phần sau
  • Cắt biến $_POST['tags'] bằng hàm explode theo dấu phẩy và lưu mảng cắt được vào mảng $arrTags
  • Dùng foreach để duyệt các phần tử $arrTags, mỗi lần duyệt, ta lấy một phần tử query vào table Tags, nếu tag đã tồn tại ta lấy ID của nó ra và lưu vào biến $idTag, nếu query không được, ta tiến hành insert dữ liệu vào table Tags, rồi lấy ID của record vừa mới thêm, lưu vào biến $idTag. Có được $idArticle và $idTag, ta insert nó vào table Articles_Tags.

Ví dụ cho trực quan đơn giản dễ hiểu:

//Insert bài viết
mysql_query(“INSERT INTO articles (title, content, tags) values ($_POST[title], $_POST[content], $_POST[tags])”);

//Lấy id của bài viết vừa mới thêm vào
$idArticle = mysql_insert_id();

//Cắt chuổi tags đưa vào mảng
$arrTags = explode(“,”, $_POST['tags']);

//Duyệt từng phần tử của Tags

foreach ($arrTags as $tag)
{
    //Hàm trim để cắt bỏ khoảng trắng
    $tag = trim($tag);

   //Lấy id của tag có tên là $tag, nếu ko có thì thêm mới
   $result = mysql_query(“select id from tags where name= ‘$tag’ limit 0,1 ”);
   if (mysql_num_rows($result) > 0)
   {
        $idTag = mysql_result($result, 0, 0);
   }
   else
   {
         mysql_query(“insert into tags(name) values ($tag)”);
        $idTag = mysql_insert_id();
   }

  //Insert dữ liệu vào table Articles_Tags
  mysql_query(“insert into articles_tags value ($idArticle, $idTag)”);

}

Update

  • Có thể sử dụng lại giao diện của phần insert và hiển thị thông tin bài viết ra đó.
  • Khi người dùng submit, ta thực hiện lệnh xóa dữ liệu bên table Articles_Tags theo điều kiện article_id bằng id của bài viết hiện tại, sau đó tiến hành update bài viết, rồi tiến hành các bước cắt chuỗi $_POST[tags] tương tự như ở bước insert.

Ví dụ:

//Giả sử id của bài viết đang sửa là: $idCurrent;

//Delete dữ liệu bên table Articles_Tags
mysql_query(“DELETE FROM articles_tags where article_id = $idCurrent”);

//Update bài viết
mysql_query(“UPDATE articles SET title =…, content = … WHERE ID = $idCurrent”);

//Cắt chuổi tags đưa vào mảng
$arrTags = explode(“,”, $_POST['tags']);

//Duyệt từng phần tử của Tags

foreach ($arrTags as $tag)
{
    $tag = trim($tag);

   //Lấy id của tag có tên là $tag, nếu không có thì thêm mới
   $result = mysql_query(“select id from tags where name= ‘$tag’ limit 0,1 ”);
   if (mysql_num_rows($result) > 0)
   {
        $idTag = mysql_result($result, 0, 0);
   }
   else
   {
         mysql_query(“insert into tags(name) values ($tag)”);
        $idTag = mysql_insert_id();
   }

  //Insert dữ liệu vào table Articles_Tags
  mysql_query(“insert into articles_tags value ($idCurrent, $idTag)”);

}

Delete
Rất đơn giản, ta chỉ cần xóa hết thông tin liên quan đến bài viết trong cả 3 table.

Bước 3: Hiển thị bài viết

  • Dưới mỗi bài viết, ta hiển thị các tag của bài viết, với mỗi tag đó ta sẽ query vào table Articles_Tags để lấy được danh sách các tag liên quan đến bài viết đó ra để khi người dùng click vào tag thì sẽ hiển thị được các bài viết có gắn tag đó.

Trên đây là hướng dẫn tạo tag mỹ miều yêu kiều và cơ bản. Chúc các bạn tag tốt! 💞 💫✨😆

SỬ DỤNG EMMET PLUGIN ĐỂ TĂNG TỐC LẬP TRÌNH HTML

Emmet là gì?

  • Emmet là một plugin cho các code editor phổ biến như Sublime text, atom, bracket,…
  • Emmet giúp việc code html và css nhanh hơn và đơn giản hơn. Thay vì phải gõ từng tag, mở tag, đóng tag, copy và paste, emmet định nghĩa các cú pháp kiểu viết tắt và sẽ tự động chuyển thành code hmtl/css.

Cài đặt Emmet trong Sublime Text

  • Mở Sublime Text, Nhấn tổ hợp phím Ctrl + Shift + P (hoặc theo đường dẫn: Preferences => Package Control).
  • Gõ dòng lệnh Install Package rồi nhấn Enter.
  • Tiếp tục gõ emmet để cài đặt plugin Emmet, sau đó nhấn Enter, chờ trong vài giây thì plugin sẽ được cài đặt vào máy.

Các cú pháp cơ bản trong EMMET

Lưu ý: Tất cả cú pháp viết xong đều nhấn phím Tab để thực thi lệnh.

1. Tạo file HTML5 mẫu

Sử dụng dấu chấm thang: ! hoặc html5

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
</body>
</html>
2. Tag

Chỉ cần viết tên thẻ + phím Tab

<p></p>
<a href=""></a>
<img src="" alt="">
...
3. Tag có Class và ID

div#header

<div id="header"></div>

div.banner

<div class="banner"></div>
4. Tag lặp lại nhiều lần

Sử dụng dấu * nhân với số thẻ

p*5

<p></p>
<p></p>
<p></p>
<p></p>
<p></p>

a.link*5

<a href="" class="link"></a>
<a href="" class="link"></a>
<a href="" class="link"></a>
<a href="" class="link"></a>
<a href="" class="link"></a>
5. Tag lồng nhau

Sử dụng dấu >

ul>li

<ul>
    <li></li>
</ul>

ul>li*5

<ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>
6. Tag kèm thuộc tính

Sử dụng cặp dấu [] với cú pháp sau: tên thẻ[thuộc tính=giá trị]:

a[href=# target=_blank]

<a href="#" target="_blank"></a>

input[type=text name=email placeholder='your email']

<input type="text" name="email" placeholder="your email">
7. Nội dung trong Tag

Sử dụng cặp dấu {}

p{Đây là nội dung thẻ p}

<p>Đây là nội dung thẻ p</p>
8. Tag tự phát sinh nội dung

lorem

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sed nesciunt dolor eos soluta at illum adipisci commodi quia distinctio sint, veniam officiis quaerat ullam delectus autem blanditiis ab esse cupiditate.

Có thể sử dụng số lượng từ trong nội dung phát sinh tùy theo nhu cầu người sử dụng

lorem10

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit, deserunt.

lorem20

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sit optio ea, numquam pariatur recusandae modi rem ipsa magnam quaerat amet!
9. Tự động đánh số cho nội dung hoặc thuộc tính

Sử dụng dấu $

ul>li*3>a.item${Menu$}

<ul>
    <li><a href="" class="item1">Menu1</a></li>
    <li><a href="" class="item2">Menu2</a></li>
    <li><a href="" class="item3">Menu3</a></li>
</ul>

h$[title=item$]{Header $}*3

<h1 title="item1">Header 1</h1>
<h2 title="item2">Header 2</h2>
<h3 title="item3">Header 3</h3>

Như các bạn thấy, dấu $ được tự động đánh số theo giá trị tăng dần từ 1 đến 3
Để đánh số bắt đầu từ số bất kì, sử dụng: $@i => với i là số bất kì bạn muốn bắt đầu

ul>li*3>a.item$@4{Menu$@}

<ul>
    <li><a href="" class="item4">Menu4</a></li>
    <li><a href="" class="item5">Menu5</a></li>
    <li><a href="" class="item6">Menu6</a></li>
</ul>

Để số thứ tự nội dung hoặc thuộc tính giảm dần, sử dụng $@-
ul>li*3>a.item$@-{Menu$@-}

<ul>
    <li><a href="" class="item3">Menu3</a></li>
    <li><a href="" class="item2">Menu2</a></li>
    <li><a href="" class="item1">Menu1</a></li>
</ul>
10. Tag ngang hàng

Sử dụng dấu + để biểu diễn các Tag ngang hàng nhau

p+a+img

<p></p>
<a href=""></a>
<img src="" alt="">
11. Tag chứa nhiều Tag con cùng cấp

.content>.content-left+.content-right

<div class="content">
    <div class="content-left"></div>
    <div class="content-right"></div>
</div>
12. Gom nhóm các tag

Sử dụng cặp dấu ngoặc tròn () để gom nhóm những tag cùng bậc

.content>(.content-left>a[href=#]{Content-left})+(.content-right>p>lorem10)

<div class="content">
    <div class="content-left"><a href="#">Content-left</a></div>
    <div class="content-right">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsa, aliquid!</p>
    </div>
</div>

.news.(.new-images>a[href=#]>img[src=#])+(.new-name>(h3{Title name})+(p>lorem10))

<div class="news ">
    <div class="new-images"><a href="#"><img src="#" alt=""></a></div>
</div>
<div class="new-name">
    <h3>Title name</h3>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui, quia?</p>
</div>
13. Lùi ra một cấp bậc

Sử dụng dấu ^ để lùi thẻ ra một bậc trước đó. Tùy từng trường hợp, có thể sử dụng dấu ^ thay cho + sẽ giúp cho đoạn code đơn giản và nhanh hơn.

.header>a>img^p>lorem10

<div class="header">
    <a href=""><img src="" alt=""></a>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente, tenetur.</p>
</div>
14. Tag ngầm

.class

<div class="class"></div>

em>.class

<em><span class="class"></span></em>

ul>.class

<ul>
    <li class="class"></li>
</ul>

table>.row>.col

<table>
    <tr class="row">
        <td class="col"></td>
    </tr>
</table>

Lưu ý

Trên đây là những cú pháp cơ bản nhất của Emmet nhằm hỗ trợ việc code HTML. Còn rất nhiều cách viết hay nữa của Emmet. Để biết thêm, các bạn có thể tham khảo tại trang: http://docs.emmet.io/cheat-sheet/

Tạo url cho site sử dụng slugify

Vấn đề

  • Khi load bài ra ngoài view chúng ta thường có url dạng http://2015vuicuoi.net/post/41 điều này rất mất quan đô thị :)))) và không đảm bảo sự chuyên nghiệp
  • Các site hay gặp thường có ur như sau : http://vnexpress.net/tin-tuc/the-gioi/tu-lieu/chiec-xe-bieu-tuong-moi-cho-suc-manh-my-tren-chien-truong-3274441.html
    -> Giải pháp Tạo url cho site sử dụng slugify

Tính năng

  • Loại bỏ tất cả các ký tự đặc biệt từ một chuỗi (VD : tình -> tinh ).
  • Chuyển đổi một chuỗi thành một slug (VD : "Tình yêu bắt đầu" -> "tinh-yeu-bat-dau").

Cài đặt

B1: Cài đặt thư viện của slugify thông qua composer
** Mở file composer.json thêm đoạn code này :
"cocur/slugify": "~1.0" vào require,sau khi thêm sẽ có dạng :

"require": {
        "php": ">=5.4.0",
        "sifoni/sifoni": "~2.0",
        "knplabs/console-service-provider": "~1.0",
        "facebook/php-sdk-v4": "~5.0",
        "cocur/slugify": "~1.0"
    },

B2: Chạy update composer thông qua lệnh : composer update -vvv
B3: Mở file hook.php trong folder config và thêm đoạn code này :

$app->register(new Cocur\Slugify\Bridge\Silex\SlugifyServiceProvider());

Như vậy bạn đã hoàn tất việc cài đặt,bây giờ tiến hành sử dụng :
VD : Mình tạo url cho post khi thêm bài viết thông qua title thì tiến hành như sau :
Trong controller tiến hành lấy giá trị từ form về như bình thường : $input = $this->getPostData();
Bây giờ mình tạo ra 1 biến url để lưu slug :

$url= $this->app['slugify']->slugify($input['title']);

Như vậy biến $url đang lưu url dưới dạng chúng ta cần
VD : Nhập tiêu đề là "Ai BD số 1 công ty" -> biến $url ="ai-bd-so-1-cong-ty"
Tiến hành add vào csdl như bình thường (Lưu ý các bạn phải tạo ra 1 cột trong table để lưu trữ biến $url)
Link tham khảo : https://github.com/cocur/slugify

DB::beginTransaction() DB::commit()

Khì tác vụ cần chạy nhiều dòng insert, update (vài trăm dòng :D) như Import từ file Excel. Thì ta đặt toàn bộ code xữ lý của Model giữa 2 câu lệnh sau.

DB::beginTransaction();
// code model
DB::commit();

use Sifoni\Model\DB;
Mới thử insert 1000 dòng mà mất có 2 3 giây :D

Fix lỗi giao thức https khi up project sifoni lên c9.io

Vấn đề

Khi chúng ta viết 1 đoạn code ajax có chứa đường dẫn url tự động phát sinh, ví dụ:

<script type="text/javascript">
$('.add-to-cart').on('click', function ()
            {
              var idp = $(this).data('id');
              $.ajax({
                      url: '{{ url('cartadd') }}',
                    type : 'post',
                    dataType : 'text',
                    data: {
                        id: idp,
                    },
                    success : function (result){
                        $('#cart-info').html(result);
                                           }
                });
            });

mặc định giao thức của đường dẫn url: '{{ url('cartadd') }} là http. Khi up lên c9.io sẽ tự động bị sửa đổi thành https, và như thế đường dẫn thay đổi, không thể thực thi được.

Hướng xử lý

Ta thêm các dòng code sau vào file hook.php, và chỉ cần thêm vào file trên c9.io, không cần thêm vào khi thực thi ở localhost:

<?php
// Only C9.io
use Symfony\Component\HttpFoundation\Request;

$app = \Sifoni\Engine::getInstance()->getApp();
$app->register(new Cocur\Slugify\Bridge\Silex\SlugifyServiceProvider());
// Hook anything you want :)

// Only C9.io
Request::setTrustedProxies(array('10.0.0.0/8'));
$app['request_context']->setScheme('https');

Như vậy các đường dẫn của chúng ta đã có thể thực thi bình thường. Goodluck!

redirect and link

Có 2 vấn đề cần giúp đỡ. hjx mọi người giúp với nhen! ( Fw sifoni)

  • view
form style="float:left" method="post" action="danh_muc"
                            input type="hidden" value="{{c.id}}" name="id"
                                button type="submit" class="gree"
  • routing có
    '/danh_muc'=> 'admin\ProductcategoryController:getlist:listcategory::post',
    • controller có function
      public function listcategoryAction()
      {
      if($this->getPostData())
      {
      $postdata=$this->getPostData();
      $id=$postdata['id'];
      }
      }

2 vấn đề:

  1. bên 1 function khác mình muốn gọi
    return $this->redirect('listcategory')+ thêm truyền biến id qua để function listcategoryAction() có thể nhận được id. method="post" nha mọi người :)
  2. Có cách nào gọi tên (listcategory ) thay vì đường dẫn( danh_muc ) ngoài view ( twig) không vậy mọi người.

thanks !

Refactor admin controllers

Các bạn nên refactor những đoạn code có số lần lặp lại > 2 lần


Ví dụ điển hình là viết các controller admin, các bạn thường viết đoạn sau đầu mỗi method (quá dư thừa, cần refactor lại)

        $nameAdmin = $this->app['session']->get('adminName', '1');
        if ($nameAdmin == 1) {
            return $this->redirect('login-admin');
        }

Giải pháp : dùng OOP chẳng hạn

  • tạo 1 file là AuthedController.php có nội dung như sau
<?php
abstract class AuthedController extends Base {
    protected $authed = null;   

    public function __construct() {
        parent::__construct();
        if ($authed = $this->app['session']->get('admin_authed', false)) {
            $this->authed = $authed;
        } else {
            $response = $this->redirect('admin_auth_signin');
            $response->send();
            return $this->app->terminate($this->request, $response);
        }
    }
}

với admin_auth_signin là route Signin vào admin , vào $this->authed sẽ là biến lưu instance User đăng nhập vào hệ thống mà trong Controller Signin $this->app['session']->set('admin_authed', $user);

Công việc là chỉ cần extends AuthedController những Controller muốn bảo vệ Admin là xong. ví dụ

<?php
namespace App\Controller\Admin;

class CategoryController extends AuthedController {
}

Xử lý đa ngôn ngữ trong sifoni

Vấn đề

  • Đặt routing và lấy $lang (ngôn ngữ) để index dữ liệu từ database.
  • Interface theo $lang.

Giả thuyết.

  • Giả thuyết 1 : http://vietuc.smo.vn/vi/,http://vietuc.smo.vn/en/. \vi \en là biến $lang dùng để phân biệt ngôn ngữ của người dùng mà render đúng với ngôn ngữ đó.
    Vậy giả sử site mình có 100 ngôn ngữ thì mình phải đặt 100 routing cho phần trang chủ à .
  • Giả thuyết 2 : interface là những thứ render mà nó dữ liệu không tồn tại trên database tìm kiếm của search kết nối với chúng tôi ở footer, ... .Thông thường chúng ta sẽ dùng if trong view để làm cho việc nài. Nhưng nếu site mình có 100 thì sao 😄 .

Để giải quyết vấn đề nài chúng ta chỉ cần......
Xem phần bên dưới ⬇️

Cách giải quyết

Trong /config/hook.php

Thêm vào đoạn code sau :

$app->before(function (Request $request, Application $app) {

    $request->setDefaultLocale($app['config.app.languages'][0]);
    $app['request_context']->setParameter('_locale', $request->getLocale());

}, 8); // 1 - 15

Trong /config/app.php

Thêm đoạn code sau :

return array(
    'languages' => array('vi', 'en', 'ja', 'zh')

Trong đó 'vi', 'en', 'ja', 'cn' là ngôn ngữ dùng trong site ,mặc định khi vào http://vietuc.smo.vn/ thì $lang = 'vi'.
Đến đây thì trong routing mình không cần quan tâm biến $lang nữa nhé . Mình sẽ đặt routing như site 1 ngôn ngữ.

Controller

Tạo CommonController.php

abstract class CommonController extends Base{
        public $data = [];
        public function __construct() {
            parent::__construct();
            $this->data = $this->Block();
            $this->data['lang'] = $this->lang = $this->request->getLocale();  // lấy $lang
            $this->app['translator']->setLocale($this->lang);             // dùng {{ |trans }} trong view

        }

Khi sử dụng các Controller khác thì extends CommonController để lấy data['lang']. Có data['lang'] rồi thì tự tin index trên database nhé.

/app/language

  • Tạo en.php để chứa từ cần dịch như Tìm kiếm, Liên lạc với chúng tôi, VĂN PHÒNG , ... sang tiếng anh. Sau đó cho đoạn code nài vào :
return [
    'messages' => [
        'search ' => 'Search',     // key dùng trong view 
        'contact_us' =>'Contact Us', // value hiển thị
        'office' => 'Office',


    ],
    'validators' => []
];
  • Khuyến cáo nên dùng key chữ thường , khoảng trống thay bằng _
  • Tương tự nếu có 100 ngôn ngữ thì bạn phải tạo 100 file .php với tên tương ứng trong array language trong /config/app.php

View

{{ 'office'| trans }}

Hãy test thử ngay để thấy sự tuyệt vời và tiện lợi của đa ngôn ngữ trong sifoni 😺

build Tree trong sifoni

1.Vấn Đề

Chúng ta có một mảng như sau :

Array
(
    [1] => Array
        (
            [id] => 1
            [name] => Tin Tức
            [parent_id] => 0
        )

    [2] => Array
        (
            [id] => 2
            [name] => Công Nghệ
            [parent_id] => 0
        )

    [3] => Array
        (
            [id] => 3
            [name] => Tin Trong Nước
            [parent_id] => 1
        )

    [4] => Array
        (
            [id] => 4
            [name] => Tin Nước Ngoài
            [parent_id] => 1
        )

    [5] => Array
        (
            [id] => 5
            [name] => Điện Thoại
            [parent_id] => 2
        )

    [6] => Array
        (
            [id] => 6
            [name] => Laptop
            [parent_id] => 2

Mảng này rất hay gặp khi chúng ta truy vấn dữ liệu từ bảng category ra để load dữ liệu lên menu.Với menu cấp 1 chính là những thành phần có parent_id = 0 và menu con của nó là những thành phần có parent_id = id danh mục chính.
Giờ ta làm gọn lại mảng này để có dạng như sau :

Array
(
     1 => Array
        (
            [id] => 1
            [parent_id] => 0
            [name] => Tin Tức
            [children] => Array
                (
                    3=> Array
                        (
                            [id] => 3
                            [name] => Tin Trong Nước
                            [parent_id] => 1
                     4 => Array
                        (
                            [id] => 4
                            [name] => Tin Trong Quốc Tế
                            [parent_id] => 1
                        )
                )
    2 => Array
        (
            [id] => 2
            [name] => Công Nghệ
            [parent_id] => 0
            [children] => Array
                (
                    5 => Array
                        (
                            [id] => 5
                            [name] => Điện Thoại
                            [parent_id] => 2
                        )
                )
                                (
                    6 => Array
                        (
                            [id] => 6
                            [name] => Laptop
                            [parent_id] => 2
                        )
                )
        )

2.Phương Pháp

Sử dụng build Tree (thực tế là kỹ thuật lập trình đệ quy).Các bạn có thể tìm hiểu thêm về kỹ thuật lập trình đệ quy để hiểu hơn về phương pháp này.

3.Tiến Hành

Ở đây mình lấy 1 ví dụ mà hầu như mọi người hay sử dụng đó là load menu cho website
B1. Xây dựng hàm buildTree
Mở file hook.php trong thư mục config thêm đoạn code có nội dung như sau :

function buildTree(array $elements, $parentId = 0, $parent_id_field = 'parent_id') {
    $branch = array();

    foreach ($elements as $element) {
        if ($element[$parent_id_field] == $parentId) {
            $children = buildTree($elements, $element['id']);
            $element['children'] = empty($children) ? [] : $children;
            $branch[] = $element;
        }
    }

    return $branch;
}

Hàm này có 4 thông số truyền vào trong đó $elements chính là toàn bộ dữ liệu lấy ra ở bảng category dưới dạng mảng
B2. Sử dụng buildTree để gom dữ liệu trong model
Trong model category chúng ta tạo hàm 1 như sau :

  public function getTreeMenu() {
    $cats = Category::where('status','=','1')->get()->toArray();
    $tree = buildTree($cats);
    return $tree;
  }

B3. Controller lấy dữ liệu rồi đưa ra view
Trong Controller bạn lấy dữ liệu menu bằng $data['cats'] = Category::getTreeMenu(); Ở đây
mình sử dụng 1 file BlockController riêng để xử lý dữ liệu riêng cho những phần layout cố đinh.

<?php
namespace App\Controller;

use Sifoni\Controller\Base;
use App\Model\Post;
use App\Model\Category;

class BlockController extends Base
{
    public function renderMainMenuAction() {
        $data['cats'] = Category::getTreeMenu();
        return $this->render('default/block/main_menu.html.twig', $data);
    }

Như vậy dữ liệu xuất ra tại main_menu.html.twig.Dưới đây là mã code mình dùng

<div class="container">
    <ul>
        {% for c in cats %}
        <li><a href="/threads/{{c.target}}">{{c.name}}</a>
            <ul>
                {% for cc in c['children'] %}
                <li><a href="/threads/{{cc.target}}">{{cc.name}}</a></li>
                {% endfor %}
            </ul>
        </li>
        {% endfor %}
    </ul>
</div>

Mục đích mình sử dụng file main_menu.html.twig để render dữ liệu cho menu là để làm nó tách biệt cũng như giảm tải dòng code.Còn nếu các bạn không muốn phức tạp thì có thể lấy dữ liệu menu bằng $data['cats'] = Category::getTreeMenu(); trong controller và đỗ dữ liệu ra view bình thường.Nếu bạn sử dụng cách này thì có thể dừng lại tại đây là không cần làm tiếp bước dưới.

B4. Tạo routing render menu

Ở file routing.php bạn thêm dòng sau

 '/_block/main_menu' => 'BlockController:renderMainMenu:block_main_menu', 

B5. Đưa vùng render riêng cho menu vào layout

Ở file phân vùng block cho layout bạn gọi tới routing render cho menu.VD :

{% endblock %}
{% block navmenu %}
    {{ render(url('block_main_menu')) }}
{% endblock %} 

Như vậy bạn đã có được một cách để đỗ dự liệu cho menu mới.

4.Mở Rộng

Các bạn có thể sử dụng phương pháp này để áp dụng với trường hợp load các bài post theo category
Chúc các bạn thành công !

Sử dụng GULP để auto refresh browser và minify (css, js, image)

Giới thiệu về gulp

  • Gulp là một bộ task runner giúp chúng ta tự động hoá một số thao tác thường gặp trong quá trình làm việc, ví dụ: refresh browser, compile và kiểm lỗi các file javascript, minify các file javascript, css, nén ảnh để đưa lên môi trường production,…
  • Gulp khá giống với batch/bash script trên Windows và Unix/Linux/Mac. Có nghĩa là chúng ta chỉ cần tạo ra một file gulp với các task khai báo bên trong và sau đó chỉ cần chạy file này là mội việc lằng nhằng ở trên sẽ được giải quyết một cách tự động, nhờ vào nguồn plugin phong phú từ cộng đồng.

Hướng dẫn cài đặt và sử dụng gulp

Cài đặt Npm

  • Để sử dụng Gulp, máy tính cần phải được cài đặt node.js và npm. Các bạn có thể download node.js theo địa chỉ sau: https://nodejs.org/en/download/
  • Khi cài đặt node.js thì công cụ quản lý packages npm cũng được cài đặt theo, nên ta không cần phải cài đặt thêm.

Cài đặt Gulp

  • Ta cần cài đặt Gulp thông qua npm ở cả global:
    npm install gulp -g
    và trong thư mục của dự án:
    npm install gulp --save-dev
  • Bước cài đặt global chỉ cần làm 1 lần duy nhất trên 1 máy tính. Còn bước cài đặt trong thư mục dự án thì bắt buộc cài khi tạo một dự án mới.
  • Để sử dụng Gulp trong một dự án, bạn chỉ cần tạo ra file gulpfile.js và lưu trong thư mục gốc. Một file gulpfile.js sẽ chứa nhiều task thực hiện các công việc khác nhau mà ta đã chỉ định trước. Nội dung một file gulpfile.js cơ bản như sau:
var gulp = require('gulp');
gulp.task('default', function(){
   // Default task code
});
gulp.task('tên-task', function() {
   // xử lý task 
});
  • Mặc định khi chạy lệnh gulp trong command line không kèm theo tham số nào, gulp sẽ tự động chạy task mặc định là default. Khi muốn chạy một task cụ thể nào đó, ta gõ lệnh: gulp tên-task

Hướng dẫn sử dụng module browser-sync để tạo auto refresh browser

  • Module này có chức năng tự động refresh lại trình duyệt ngay sau khi bạn sửa đổi các file javascript, css hay html trong dự án.
  • Ta tiến hành cài đặt module browser-sync bằng công cụ npm. Từ giao diện comand line của npm, ta tiến hành gõ vào lệnh npm install browser-sync --save-dev như hình sau:
    browser-sync
  • Sau đó tạo file gulpfile.js ngay trong thư mục project của bạn, cấu trúc cây thư mục sẽ có dạng như sau
    folder
  • Tiếp đến, ta khai báo sử dụng gói browser-sync cho chương trình:
var browserSync = require('browser-sync');
  • Và tạo ra một task mới, đặt tên là serve với nội dung như sau:
gulp.task('serve', [], function () {
    browserSync({
        notify: false,
        server: {
            baseDir: '.'
        }
    });
    gulp.watch(['*.html'], reload);
    gulp.watch(['assets/js/*.js'], reload);
    gulp.watch(['assets/css/*.css'], reload);
    gulp.watch(['assets/images/*.css'], reload);
});
  • Để chạy file gulp này, chúng ta gõ lệnh: gulp serve
  • Trình duyệt sẽ tự bật lên với địa chỉ http://localhost:3000
  • Bây giờ, bạn thử sửa một file html hoặc javscript, css bất kì, và lưu lại, các trình duyệt đang mở sẽ tự động refresh lại và cập nhật thay đổi đó ngay lập tức.

Cài đặt các module thực hiện việc minify

  • Ta cũng sử dụng npm như trên, thực thi lần lượt các câu lệnh sau để tiến hành cài đặt các module
npm install gulp-minify-css --save-dev
npm install gulp-minify --save-dev
npm install gulp-imagemin --save-dev
npm install imagemin-pngquant --save-dev
  • Tạo ra một task mới, đặt tên là compress với nội dung như sau:
gulp.task('compress', function() {
  //cấu hình minify js
  gulp.src('assets/js/*.js') //đường dẫn đến thư mục chứa các file js
    .pipe(minify({
        exclude: ['tasks'],
        ignoreFiles: ['-min.js'] //những file không muốn nén
    }))
    .pipe(gulp.dest('dist/js')); //thư mục dùng để chứa các file js sau khi nén
  //cấu hình minify css
  gulp.src('assets/css/*.css') //đường dẫn đến thư mục chứa các file css
    .pipe(minifyCss({compatibility: 'ie8'}))
    .pipe(gulp.dest('dist/css')); //thư mục dùng để chứa các file css sau khi nén
  //cấu hình minify image
  gulp.src('assets/images/*') //đường dẫn đến thư mục chứa các file images
    .pipe(imagemin({
        progressive: true,
        svgoPlugins: [{removeViewBox: false}],
        use: [pngquant()]
    }))
    .pipe(gulp.dest('dist/images')); //thư mục dùng để chứa các file images sau khi nén
});
  • Để thực thi task này (chỉ nên thực thi sau khi đã hoàn tất project), chúng ta gõ lệnh: gulp compress
  • Trong thư mục project của bạn sẽ xuất hiện thêm một thư mục dist (chứa các file đã được nén) như hình dưới
    compress

File gulpfile.js hoàn chỉnh như sau

var gulp = require('gulp');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var minify = require('gulp-minify');
var minifyCss = require('gulp-minify-css');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');

gulp.task('serve', [], function () {
    browserSync({
        notify: false,
        server: {
            baseDir: '.'
        }
    });
    gulp.watch(['*.html'], reload);
    gulp.watch(['assets/js/*.js'], reload);
    gulp.watch(['assets/css/*.css'], reload);
    gulp.watch(['assets/images/*.css'], reload);
});

gulp.task('compress', function() {
  //cấu hình minify js
  gulp.src('assets/js/*.js') //đường dẫn đến thư mục chứa các file js
    .pipe(minify({
        exclude: ['tasks'],
        ignoreFiles: ['-min.js'] //những file không muốn nén
    }))
    .pipe(gulp.dest('dist/js')); //thư mục dùng để chứa các file js sau khi nén
  //cấu hình minify css
  gulp.src('assets/css/*.css') //đường dẫn đến thư mục chứa các file css
    .pipe(minifyCss({compatibility: 'ie8'}))
    .pipe(gulp.dest('dist/css')); //thư mục dùng để chứa các file css sau khi nén
  //cấu hình minify image
  gulp.src('assets/images/*') //đường dẫn đến thư mục chứa các file images
    .pipe(imagemin({
        progressive: true,
        svgoPlugins: [{removeViewBox: false}],
        use: [pngquant()]
    }))
    .pipe(gulp.dest('dist/images')); //thư mục dùng để chứa các file images sau khi nén
});

Sử dụng skeleton

https://github.com/tanUIT/generators-web-project

nodejs simple

Cài đặt NodeJS

Download tại đây


Tải các package cần thiết

- Tạo 1 file: `package.json` với nội dung
{
    "name" : "chat-realtime",
    "version" : "0.0.1",
    "private" : "true",
    "dependencies" : {
        "socket.io" : "1.4.5",
        "express": "4.13.4"
    }
}
  • Chạy lệnh npm install để tiến hành cài đặt.

Tạo server

var express = require('express'),
        app = express(),
        server = require('http').createServer(app),
        io = require('socket.io').listen(server),
        port = process.env.PORT || 3000;

server.listen(port);

Sự kiện trên Server

io.sockets.on('connection', function(socket) {
        // code xử lý khi client kết nối đến server

        // gửi dữ liện cho client
        io.to(socket.id).emit('event_name', data);

        // gửi dữ liệu cho toàn bộ client
        io.sockets.emit('event_name', data);

        // client join 1 room
        socket.join(room);

        // client leave 1 room
        socket.leave(room);

});

io.sockets.on('disconnect', function(socket) {
        // code xử lý khi client ngắt kết nối đến server
});

Sự kiện tại Client

<script src="/socket.io/socket.io.js"></script>
<script>
        var socket = io.connect();

        socket.on('event_name', function(data) {
                // code xữ lý khi nhận được emit từ Server
        });

        // emit đến Server
        socket.emit('event_name', data);

</script>

Chạy Server

node app.js


Live Demo
Source Code Demo

Tối ưu hình ảnh trên web bằng TimThumb (mà có link đẹp)

Để tối ưu hóa hình ảnh (một số hình ảnh được up lên tám chục MB mà lúc lấy ra sài thỉ chỉ có 200px (chỉ còn khoảng 80 KB), thế là hoang phí cuộc đời.

Để tối ưu ta có thể dùng TimThumb một cách khéo léo mà không để lại tí link xấu nào như http://abc.xyz/timthumb.php?src=Hinh_Bay_Ba_Cam_Con_Nit.jpg&w=800&h=600


OKAY ! Lets start !!!

Chuẩn bị đồ chơi

  • Tạo folder thumb_cache ở DocumentRoot (nếu là Sifoni thì DocumentRoot = web, còn lại thì public_html)
  • Tạo file _timthumb_.php với nội dung như file này

Magic happens : hắt-tê-ét-sét

  • Mở file .htaccess ra, copy đoạn sau bỏ vào sau dòng RewriteEngine On, nhớ có 2 dòng trắng đầu và cuối bên dưới nhé

RewriteRule ^thumbw\/(\d+)\/(.*)$ _timthumb_.php?src=$2&w=$1 [QSA,L]
RewriteRule ^thumbh\/(\d+)\/(.*)$ _timthumb_.php?src=$2&h=$1 [QSA,L]
RewriteRule ^thumb\/(\d+)\/(\d+)\/(.*)$ _timthumb_.php?src=$3&w=$1&h=$2 [QSA,L]

Hướng dẫn sử dụng

  1. Khi hình ảnh có kích thước Rộng x Cao cố định

/uploads/Hinh_Bay_Ba.jpg thành /thumb/RONG/CAO/uploads/Hinh_Bay_Ba.jpg

  1. Khi hình ảnh có chiều Rộng cố định

/uploads/Hinh_Bay_Ba.jpg thành /thumbw/RONG/uploads/Hinh_Bay_Ba.jpg

  1. Khi hình ảnh có chiều Cao cố định

/uploads/Hinh_Bay_Ba.jpg thành /thumbh/CAO/uploads/Hinh_Bay_Ba.jpg

Lưu ý : RONGCAO là số nguyên zin, không bỏ thêm bất kỳ ký tự gì như px, cm

MEME

Phân quyền người dùng đơn giản trong Sifoni

Phân quyền người dùng đơn giản

Vấn đề

Một trang admin quản lý hệ thống và nội dung phức tạp yêu cầu có nhiều nhóm người dùng với các quyền hạn khác nhau về việc truy cập cũng như thực hiện các chức năng.

Các nhóm người dùng cơ bản:

  • Super Admin: Nắm quyền quản trị cao nhất, có quyền xóa người dùng trong nhóm Admin.
  • Admin: Có quyền sử dụng toàn bộ tính năng trong trang admin, trừ chức năng quản lý Admin user.
  • Editor: Có quyền đăng nội dung và quản lý nội dung của người dùng khác.
  • Author: Có quyền đăng nội dung nhưng chỉ được phép quản lý nội dung của bản thân.
  • User: Chỉ có quyền chỉnh sửa thông tin cá nhân của mình.

Giải pháp

  • Khai báo các nhóm người dùng trong app/config/hook.php
...
// define Permission CONST
define('SUAD', 'super admin');
define('ADMIN', 'admin');
define('EDITOR', 'editor');
define('AUTHOR', 'author');
define('USER', 'user');
  • Tạo model app/model/admin/Permission.php
<?php
namespace App\Model\admin;

use Sifoni\Model\Base;
use App\Model\admin\User;
class Permission extends Base{

    static function checkPermission($permit, $cur_username, $cur_user_permit){
        $user=User::where('username', $cur_username)
                    ->where('permission', $cur_user_permit)->first();
        if($user){
            switch ($permit) {
                case SUAD:
                    if($cur_user_permit==SUAD)
                        return true;
                    break;
                case ADMIN:
                    if(in_array($cur_user_permit, [SUAD, ADMIN]))
                        return true;
                    break;
                case EDITOR:
                    if(in_array($cur_user_permit, [SUAD, ADMIN, EDITOR]))
                        return true;
                    break;
                case AUTHOR:
                    if(in_array($cur_user_permit, [SUAD, ADMIN, EDITOR, AUTHOR]))
                        return true;
                    break;
                default:
                    break;
            }
        }
        return false;
    }
}
  • Tạo app/controller/admin/PermissionController.php để kế thừa và sử dụng bởi các controller khác
<?php
namespace App\Controller\admin;

use App\Model\admin\Permission;
use App\Controller\admin\BlockController;
class PermissionController extends BlockController{
    static $permit=AUTHOR;

        /*
        * static::$permit  : Giấy phép người dùng thấp nhất cho chức năng muốn phân quyền.
        * $this->app['session']->get('username')  : Session chứa username đã đăng nhập.
        * $this->app['session']->get('user_permission')  : Session chứa permission của username đó.
        */
    public function __construct() {
        parent::__construct();
        if(!Permission::checkPermission(static::$permit, $this->app['session']->get('username'), $this->app['session']->get('user_permission'))){
            $response = $this->redirect('error_permission');
            $response->send();
            return $this->app->terminate($this->request, $response);
        }
    }

}
  • Tạo route error_permission(hay tên nào khác cũng được) và trang báo lỗi. Chú ý: function trả về của route này không được nằm trong controller có kế thừa PermissionController.

Cách dùng

* Phân quyền theo controller:

Ví dụ UserController kế thừa PermissionController ở trên:

...
class UserController extends PermissionController{
    static $permit=SUAD;
...
}

Gán giá trị cho $permit là nhóm người dùng thấp nhất mà bạn muốn cho phép sử dụng tất cả các function nằm trong class UserController

* Phân quyền theo function:

Vẫn cho UserController kế thừa PermissionController nhưng không khai báo biến $permit nữa
Thay vào đó, trong function muốn phân quyền, ta thêm vào như sau:

class UserController extends PermissionController{
    function createAction(){
        static::$permit=SUAD;
        parent::__construct();

                // your code
        }
...
}

Vậy là bạn đã phân quyền cho function createAction chỉ có nhóm người dùng SUAD mới được phép sử dụng.

Lưu ý: Nhóm người dùng có quyền cao hơn được quyền sử dụng tất cả các function được phân quyền cho nhóm người dùng thấp hơn.

Fix image khi share link lên fb

======Copy url=====
+Các tag meta cần thiết:

<link rel="image_src" href="[IMAGE_LINK]" />
<meta property="og:title" content="[TITLE_SHARE]" />
<meta property="og:type" content="[article/website]" />
<meta property="og:image" content="[IMAGE_LINK]" />
<meta property="og:description" content="[DESCRIPTION]" />
<meta property="og:url" content="[PAGE_LINK]" />
<meta property="article:author" content="https://www.facebook.com/[FAN_PAGE]" />
<meta name="author" itemprop="author" content="[DOMAIN_SITE]" />
<meta property="fb:app_id" content="xxxxxxxxxxxxxxxxxx" />

Ví dụ:

  • meta property="article:author" có content là link fan page, nhưng meta name="author" không xác định: thì bên dưới khung share sẽ hiển thị dạng:
TÊN.DOMAIN | BỞI TÊN.FAN.PAGE
{tên fanpage này có link về trang fan page}
  • meta property="article:author" có content là link fan page, và meta name="author" là domain website: thì bên dưới khung share sẽ hiển thị dạng:
BỞI TÊN.FAN.PAGE
{tên fanpage này có link về trang fan page}
{Giống như các bài share của trang 24h trên fb}

+Một số property khác:

- og:site_name
- og:locale : Ngôn ngữ, mặc định là en_US
- og:video : url của video, cần bổ sung og:image
- og:video:type
https://developers.facebook.com/docs/sharing/opengraph/object-properties

+Link tạo App facebook:

https://developers.facebook.com/apps

+Kích hoạt app

Trong phần Setting, nhập đầy đủ App Domains, Contact Email
Trong phần Status & Review, kích hoạt App

+Link debug url cần share:

https://developers.facebook.com/tools/debug/

Sau khi debug, url sẽ được facebook sẽ lưu vào cache.
Sau đó, có thể copy url lên facebook để share.

+Code js auto refresh cache của fb:

<script>
  $.post('https://graph.facebook.com', {id: document.URL, scrape: true}, function (response) {});
</script>

Đặt code này vào trang cần refresh cache.
Như vậy sau khi mở bài viết lên, đem link đi share sẽ hiện đầy đủ thông tin trong thẻ meta.

Dùng elFinder trong Sifoni để thao tác với file và folder

Link repository: https://github.com/huynhtrucquyen0812/elfinder-for-sifoni

Mục đích

  1. Tích hợp File Browser vào CKEditor
  2. File Browse cho form cơ bản

Cài đặt

  • Kiểm tra file php.ini đã bỏ comment dòng extension=php_fileinfo.dll
  • Thêm vào require trong composer.json đoạn code:
"studio-42/elfinder": "2.x-dev",
 "barryvdh/elfinder-flysystem-driver": "~0.1”

rồi chạy lệnh composer update -vvv

  • Sửa file routing trong phần /admin
'/finder/browse' => 'Admin\FinderController:browse:admin_finder_browse',
'/finder/connector' => 'Admin\FinderController:connector:admin_finder_connector’,
  • Thêm file FinderController.php vào app/controller/Admin/
  • Thêm file finder.html.twig vào app/view/admin/utils/
  • Copy thư mực elfinder vào web/assets/
  • Tạo thư mục upload và chmod 777 trong web/

Cách dùng

I. Tích hợp vào CKEditor:

  1. Bằng JavaScript
<textarea name="content" id="content" rows="10" cols="60"></textarea>
<script>
        CKEDITOR.replace('content', {
            filebrowserBrowseUrl : "{{ url('admin_finder_browse') }}"
        });
</script>
  1. Bằng config.js trong ckeditor/
CKEDITOR.editorConfig = function( config ) {
    // Config Sth
    config.filebrowserBrowseUrl = '/admin/finder/browse';
};

II. File Browser trong form

<img id="img" src="/assets/admin/image/no-image.png"/> <!-- để hiển thị image được chọn -->
<input id="imgURL" type="text" name="image"/> <!-- để lưu URL của image -->
<button class="btn-browse-img" type="button">Choose</button> <!-- để browse image -->

<script>
    $(document).ready(function() {
        window.admin_finder_browse = "{{ url('admin_finder_browse') }}";
    });
    $(document).ready(function() {
    $('.btn-browse-img').on('click', function() {
            var txt = $(this);
            var fileupload = function(file) {
                $('#img').attr('src', file.url);
                $('#imgURL').attr('value', file.url)
            };
            window.upload_handle = fileupload;
            var w = window.open(window.admin_finder_browse, "", "width=600, height=400");
            w.onbeforeunload = function(){
                window.upload_handle = false;
            };
        });
    });
</script>

--> Về AuthedController trong FinderController: #3

DONE~!!! (╯°□°)╯︵ ┻━┻

Nested Set Model

Nested Set Model

Giới thiệu

nested_set_model_1

Bạn đang thiết kế 1 website hay 1 software có các chuyên mục đa cấp (có chuyên mục cha, con, cháu, chắt, chút, chít, v.v..). Làm sao để thiết kế CSDL để đạt sự tối ưu trong lưu trữ, truy vấn, chỉnh sửa đây ? Bài viết sẽ đưa ra 1 giải pháp hoàn toàn mới, và bạn sẽ thấy được tầm quan trọng của Cấu Trúc Dữ Liệu.

Các giải pháp truyền thống

  • Sử dụng 1 field parent_id để lưu giá trị id của node cha ( parent_id = 0 khi node đó thuộc root - node 'ông tổ' ). Khi đó để lấy dữ liệu ra, phải dùng đệ quy để giải quyết => performance thấp.
  • Sử dụng hệ thống multi-tables tương ứng với từng level. Hạn chế về số lượng level cố định, và số table nhiều, khó quản lý.

Giải pháp mới : Nested Set Model

Giải pháp mới này sử dụng cấu trúc cây dữ liệu như sau :

nested_set_model_2

Cây dữ liệu sẽ có những thuộc tính như sau :

  1. Mỗi node ngoài giá trị value sẽ 2 giá trị là leftright ( right > left ). Ví dụ node A ( có left là 1, right là 8).
  2. Một node con cháu thuộc 1 node cha khi và chỉ khi left con > left cha AND right con < right cha. Ví dụ C là con của A ( 2 > 1 AND 5 < 8 ) và F là con của B ( 10 > 9 AND 11 < 12 ).
  3. Node ông tổ root có left = 0 và đường đi tăng dần sẽ theo "từ trên xuống dưới, từ trái qua phải". Xem hình minh họa bên dưới để hiểu rõ hơn đường đi.

nested_set_model_3

  1. Các giá trị leftright của tất cả các node tạo thành 1 dãy số tự nhiên liên tục từ 0 đến (N*2 + 1) với N là tổng số node của cây ( không tính node root).

Các bạn có thể giúp mình viết tiếp :

  • ADD 1 node
  • DELETE 1 node
  • MOVE 1 node (move tất cả children của node theo) trong cây*
  • Implement = 1 model để tái sự dụng trong Sifoni

Vấn đề bảo vệ website khi sử dụng ckfinder

Khi sử dụng ckfinder để upload hình lên website chúng ta cần chú ý tới việc bảo vệ hệ thống tài nguyên của web.
VD khi bạn truy cập địa chỉ sau : http://thanhngutienganh.com/scripts/ckfinder/ckfinder.html bạn có thể dễ dàng nhận ra rằng mình chẳng cần phải đăng nhập hay trải qua bất kì khó khăn nào để có thể xóa và up hình lên website đó -> điều này thật sự nguy hiểm
Trong ckfinder có chứa file config.php trong đó có hàm CheckAuthentication cho phép bạn thực hiện việc kiểm tra vấn đề truy cập.Tuy nhiên việc sử dụng session trong file config.php không phải là dễ dàng vì thế hôm nay mình sẽ nói về cách dễ dàng hơn được NAM htacess mang tới,đó là dùng htacess để chặn truy cập.
File .htacess của bạn có thể viết lại như sau

<IfModule mod_rewrite.c>
    RewriteEngine on
    Options -Indexes
    RewriteRule scripts/ckfinder/ckfinder.html - [F,NC]
    RewriteRule  ^$ web/    [L]
    RewriteRule  (.*) web/$1 [L]
</IfModule>

Cùng tận hưởng kết quả nào !!!!
Chúc các bạn thành công ~!

Add Marker - Google Map (Simple) - Google Javascript API

Vào trang Console Google API để tạo API Key.
Tạo một project mới.
Enable API cần dùng. Google Maps JavaScript API
Trong tab Credentials tạo mới một API Key.


Thư viện Google Javascript API

<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=[YOUR_KEY]&signed_in=true&callback=initMap" async defer></script>

Tạo mới map.

<div id="map_canvas"></div>
function initMap(){
           var map = new google.maps.Map(document.getElementById('map_canvas'), {
                center: {
                    lat: 10.825,
                    lng: 106.685
                },
                zoom: 15
            });
}

Hàm xử lý.

        function geocoderAddress(geocoder, resultsMap, address) {
            geocoder.geocode({
                address: address
            }, function (results, status) {
                if (status === google.maps.GeocoderStatus.OK) {
                    resultsMap.setCenter(results[0].geometry.location);
                    if (marker) {
                        marker.setMap(null);
                    }
                    marker = addMarker(resultsMap, results[0].geometry.location);
                }
            });
        }

        function addMarker(map, position) {
            var mk = new google.maps.Marker({
                map: map,
                position: position
            });
            return mk;
        }

Tạo Marker.

var geocoder = new google.maps.Geocoder();
var address = "07 Nam Quoc Cang, Phuong Pham Ngu Lao, Quan 1, TP Ho Chi Minh";
geocoderAddress(geocoder, map, address);

Giảm tải website chạy Sifoni bằng cách cache twig template

Để giảm tải phần lớn khối lượng CPU & RAM cho server khi chạy production thì ta có thể cache những file mà Twig Template Engine compile từ .twig ra .php

Vào file app/config/hook.php thêm vào dòng sau :

// Twig Cache on production
if ($app['env'] == \Sifoni\Engine::ENV_PROD) {
    $app['twig.options'] = [
        'cache' => \Sifoni\Engine::getInstance()->getDirPath('cache')
    ];
}

và theo đó ta chỉ cần chỉnh trong bootstrap.php sang môi trường PROD sau khi chỉnh sửa và test xong web (nhớ tắt hoặc comment dòng này khi debug hoặc chỉnh sửa web để tránh cache)

$engine = \Sifoni\Engine::getInstance()->init()->bootstrap(array(
    'env' => \Sifoni\Engine::ENV_PROD, // Tắt mở bằng cách comment hoặc bỏ comment dòng này
    'path.root' => ROOT_PATH,
    'autoloader' => $autoloader,
));

Lưu ý chỉ sử dụng cache khi triển khai trên hosting hoặc server

BTW, FUNNY MEME
ram

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.