CakePHP – CounterCache

Przechowuje w tabeli ilość rekordów podrzędnych (dzieci). Definiowany jest na tabeli podrzędnej ( belongsTo() ). Tabela nadrzędna musi mieć kolumnę typu int (np. comment_count)

class CommentsTable extends Table
{
  public function initialize(array $config)
  {
    $this->belongsTo('Articles');

    $this->addBehavior('CounterCache' , [
    'Articles' => [ 'comment_count' ]
    ]);
  }
}

Można zastosować CounterCache w powiązaniach belongsToMany() tylko z opcją through. Wówczas behaviour definiuje się w tabeli łączącej gdyż posiada powiązania belongsTo().

Wprowadzenie warunku – zlicza tylko komentarze bez spamu

$this->addBehavior('CounterCache' , [
  'Articles' => [
    'comment_count' => [
      'conditions' => [ 'Comments.spam' => false]
    ]
  ]
]);

CakePHP – find(’list’)

class ArticlesTable extends Table
{
  public function initialize(array $config)
  {
    $this->setDisplayField('title');
  }
}
$query = $articles->find('list' , [
          'keyField'   => 'slug' ,
          'valueField' => 'title'
          'groupField' => 'author_id'
        ]);

$data = $query->toArray();
$query = $articles->find('list' , [
     'keyField' => 'id' ,
     'valueField' => 'author.name'
])->contain([ 'Authors' ]);

CakePHP – entity properties

Dostępne do zmiany przez formularz

class User extends Entity
{
  protected $_accessible = [
    'id' => false,
    '*'  => true
  ]
}
$article->accessible('title' , false);
$article->set($properties, [ 'guard' => false]);

Właściwości wirtualne uwzgl. w konwersji na array/json

class User extends Entity
{
  protected $_virtual = [ 'full_name' ];
}
$user->virtualProperties([ 'full_name' , 'is_admin' ]);

Nie będą exportowane do formatu array/json

class User extends Entity
{
  protected $_hidden = [ 'password' ];
}
$user->hiddenProperties([ 'password' , 'recovery_question' ]);

CakePHP – badanie właściwości

$article->has('title' );      // czy jest i czy ma wartość
$article->isEmpty('title' );  // czy jest pusta
$article->hasValue('title' ); // czy ma wartość

$article->isNew();  // nie zapisany jeszcze w bazie

$article->isDirty('title' ); // czy zmieniono title

$article->comments[] = $newComment;
$article->setDirty('comments' , true);  // ustaw - zmieniono
$dirtyFields = $entity->getDirty(); // pobierz wszystkie falgi

$article->clean();  // wyczyść wszystkie flagi dirty
$entity->unsetProperty('name');

$user->unsetProperty('beers');  // user belongsToMany beers
$user->has('beers');   // false
// Aby znowu mieć dostęp - ładujemy ręcznie
$user= $this->Programmers->loadInto($user, ['Beers']);

$this->request

$controllerName = $this->request->getParam('controller' );
$params = $this->request->getAttribute('params' );
$passedArgs = $this->request->getParam('pass' );

// URL is /posts/index?page=1&sort=title
$page = $this->request->getQuery('page' );
$query = $this->request->getQueryParams();

// Input with a name 'MyModel[title]' 
$title = $this->request->getData('MyModel.title' );

$host = $this->request->env('HTTP_HOST' );
$env = $this->request->getServerParams();

// /subdir/articles/edit/1?page=1
$here = $request->getRequestTarget();

$base = $request->getAttribute('base');       //  /subdir
$base = $request->getAttribute('webroot');    //  /subdir/

$jsonData = $this->request->input('json_decode' );
$data = $this->request->input('Cake\Utility\Xml::build' , [ 'return' => 'domdocument' ]);

$this->request->is('post')
$this->request->is('ajax')   // X-Requested-With = XMLHttpRequest
$this->request->is('json')   // application/xml lub text/xml  .json (ext)

$userName = $this->request->session() ->read('Auth.User.name' );
$request->domain();
$request->subdomains();
$request->host();

$request->getMethod();
$this->request->allowMethod([ 'post' , 'delete']);

WP przeniesienie hostingu

1. Przeniesienie kodu – FTP
2. Kopia bazy danych – takie same: charset, collate
3. Zmiany parametrów nowej bazy danych

# nano wp-config.php
dbname
dbuser
dbpass
dbhost

4. Zmiany domeny w tabeli wp_options:

mysql> select * from wp_options where option_id=1 or option_id=2;
+-----------+-------------+-----------------+----------+
| option_id | option_name | option_value    | autoload |
+-----------+-------------+-----------------+----------+
|         1 | siteurl     | https://old.pl   | yes      |
|         2 | home        | https://old.pl   | yes      |
+-----------+-------------+-----------------+----------+

mysql> update wp_options set option_value='https://new.pl' 
       where option_value='https://old.pl'

5. Opcjonalnie można zmienić wpisy w tabeli wp_posts. Wszystkie tabele nowej bazy danych można przeszukać ze względu na nazwę starej domeny.

php7.0 extentions

Pakiety do pracy z CakePHP3, Moodle3

# dpkg -l | grep php
.
ii  libapache2-mod-php7.0
ii  php-common
ii  php7.0
ii  php7.0-cli
ii  php7.0-common
ii  php7.0-curl
ii  php7.0-gd
ii  php7.0-intl
ii  php7.0-json
ii  php7.0-mbstring
ii  php7.0-mcrypt
ii  php7.0-mysql
ii  php7.0-opcache
ii  php7.0-readline
ii  php7.0-soap
ii  php7.0-sqlite3
ii  php7.0-xml
ii  php7.0-xmlrpc
ii  php7.0-zip         

reCaptcha

Klient – Kod HTML

-- head
<script src='https://www.google.com/recaptcha/api.js'></script>

-- form - przycisk reCaptcha
<div class="g-recaptcha" data-sitekey="62LczhlYUAAAAAAYW6nsSPsXJ-mulLH6w7TxFH4Pa"></div>

Serwer – kod PHP

if($_POST['send'])
{           
 $response = $_POST['g-recaptcha-response'];

 if($response) {

 $url = "https://www.google.com/recaptcha/api/siteverify";
 $secret = "62LczhlYUAAAAADTnGOz8GRl9c66Rwn4GDSVsChAg";
 $remoteip = $_SERVER['REMOTE_ADDR'];
 $params = [
            'secret'   => $secret, 
            'remoteip' => $remoteip, 
            'response' => $response
           ];      
  
 $post_params = http_build_query($params);     
   
 $ch = curl_init();        
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_HEADER, false);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 curl_setopt($ch, CURLOPT_POST, true);
 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_params);       
 $response = curl_exec($ch);
 curl_close($ch);

 $resp_tab = json_decode($response, true);  
  
 if( $resp_tab['success'] )
 {
   //Przetwarzanie i wysłanie formularza
   ...
   $info = "Formularz wysłany";
 } 
 else {
   $info = "Nieprawidłowa weryfikacja testu w Google. 
            Kod błęd: " .$resp_tab['error-codes'][0];
  }
 } 
 else {
     $info = "Błędny wybór lub nie wypełniłeś chptcha."; 
 }
 $smarty->assign('info', $info);
}

scroll_behavour

https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior
Płynne przewijanie zawartości pojemnika

<nav>
  <a href="#page-1">1</a>
  <a href="#page-2">2</a>
  <a href="#page-3">3</a>
</nav>
<scroll-container>
  <scroll-page id="page-1">1</scroll-page>
  <scroll-page id="page-2">2</scroll-page>
  <scroll-page id="page-3">3</scroll-page>
</scroll-container>
a {
  display: inline-block;
  width: 50px;
  text-decoration: none;
}
nav, scroll-container {
  display: block;
  margin: 0 auto;
  text-align: center;
}
nav {
  width: 339px;
  padding: 5px;
  border: 1px solid black;
}
scroll-container {
  display: block;
  width: 350px;
  height: 200px;
  overflow-y: scroll;
  scroll-behavior: smooth;
}
scroll-page {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 5em;
}

Babel

# mkdir babel_test
# cd babel_test && npm init
package name: (babel_test)
version: (1.0.0)
description:
entry point: (index.js)
test command: echo "Error: no test specified" && exit 1
git repository:
keywords:
author:
license: (ISC)
About to write to D:\JS\nodejs\babel_test\package.json:

{
  "name": "babel_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Is this ok? (yes)

Instalacja babel

# npm install --save-dev babel-cli

Podmienić w package.json właściwość scripts:

"scripts": {
    "build": "babel src -d lib"
}

Apache SSL

# cd /etc/apache2
# a2enmod ssl
# a2ensite default-ssl
# systemctl reload apache2
# mkdir /etc/apache2/ssl
# openssl req -x509 -nodes -days 1825 -newkey rsa:2048 
  -keyout /etc/apache2/ssl/apache.key 
  -out /etc/apache2/ssl/apache.crt
# chmod 600 /etc/apache2/ssl/*

# nano /etc/apache2/sites-enabled/default-ssl.conf

default-ssl.conf

<IfModule mod_ssl.c>
    <VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        ServerName bambino.pl:443
        DocumentRoot /var/www/html/bambino/webroot
        . . .
        SSLEngine on
        . . .
        SSLCertificateFile /etc/apache2/ssl/apache.crt
        SSLCertificateKeyFile /etc/apache2/ssl/apache.key
# service apache2 reload
# openssl s_client -connect intranet.int:443

HTTP Content Security Policy

Nagłówek odpowiedzi. Serwer informuje przeglądarkę które skrypty z jego domeny mogą być dołączone

Content-Security-Policy: <policy-directive>; <policy-directive<
// header
Content-Security-Policy: default-src https:

Content-Security-Policy: default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'

// meta tag
<meta http-equiv="Content-Security-Policy" content="default-src https:">

Dyrektywy:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy