Thứ Ba, 14 tháng 2, 2017

Generate APNS p12

  1. Export the private key from the Keychain and name it aps_private-key.p12.
  2. Convert the key with the following command openssl pkcs12 -nocerts -out aps_private-key.pem -in aps_private-key.p12, make sure to enter a PEM pass phrase of at least 4 characters.
  3. Download the certificate for your app from https://developer.apple.com/account/ios/identifiers/bundle/bundleList.action. The downloaded file should be called something like aps_development.cer.
  4. Convert the certificate with the following command openssl x509 -in aps_development.cer -inform der -out aps_development.pem
  5. Generate the credentials using openssl pkcs12 -export -in aps_development.pem -out aps_dev_credentials.p12 -inkey aps_private-key.pem.
  6. And I'm ready to use the credentials generated in step 5 (aps_dev_credentials.p12).
    final InputStream certificate = Thread.currentThread().getContextClassLoader()
            .getResourceAsStream("aps_dev_credentials.p12");
    final char[] passwd = {'1','2','3','4'};
    final ApnsService apnsService = com.notnoop.apns.APNS.newService()
            .withCert(certificate, new String(passwd))
            .withSandboxDestination().build();
    apnsService.testConnection();

Source: http://stackoverflow.com/questions/20077626/whats-the-correct-format-for-java-apns-certificate

Thứ Hai, 13 tháng 2, 2017

ADVANCED MULTI-MODEL FORMS IN YII2




Manage multiple models in a form, supporting validation for each model.
Consider a Product model that has a single Parcel model related, which represents a parcel that belongs to the product. In the form you want to enter information for the product and the parcel at the same time, and each one should validate according to the model rules.
Yii2 Multi-Model Form

TABLE MODELS

Start with the following tables:
CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);
CREATE TABLE `parcel` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `code` varchar(255) NOT NULL,
  `height` int(11) NOT NULL,
  `width` int(11) NOT NULL,
  `depth` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `product_id` (`product_id`),
  CONSTRAINT `parcel_product_id` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`)
);
And the models that represent the tables are:
models/Product.php
<?php
namespace app\models;

use \yii\db\ActiveRecord;

class Product extends ActiveRecord
{
    public static function tableName()
    {
        return 'product';
    }
    
    public function rules()
    {
        return [
            [['name'], 'required'],
            [['name'], 'string', 'max' => 255]
        ];
    }
    
    public function getParcel()
    {
        return $this->hasOne(Parcel::className(), ['product_id' => 'id']);
    }
}

models/Parcel.php
<?php
namespace app\models;

use \yii\db\ActiveRecord;

class Parcel extends ActiveRecord
{
    public static function tableName()
    {
        return 'parcel';
    }
    
    public function rules()
    {
        return [
            [['code', 'height', 'width', 'depth'], 'required'],
            [['product_id', 'height', 'width', 'depth'], 'integer'],
            [['code'], 'string', 'max' => 255],
            [['product_id'], 'exist', 
              'skipOnError' => true, 
              'targetClass' => Product::className(), 
              'targetAttribute' => ['product_id' => 'id']]
        ];
    }
    
    public function getProduct()
    {
        return $this->hasOne(Product::className(), ['id' => 'product_id']);
    }
}

THE FORM MODEL

Now we get to the advanced form, but don’t worry, it’s all quite straight forward. We need a model to handle loading, validating and saving the data.
The purpose of ProductForm form is to: - handle loading Product and Parcel models - assigning the submitted data to model attributes - saving data after validation - displaying error summaries on the form
models/form/ProductForm
<?php
namespace app\models\form;

use app\models\Product;
use app\models\Parcel;
use Yii;
use yii\base\Model;
use yii\widgets\ActiveForm;

class ProductForm extends Model
{
    private $_product;
    private $_parcel;

    public function rules()
    {
        return [
            [['Product'], 'required'],
            [['Parcel'], 'safe'],
        ];
    }

    public function afterValidate()
    {
        $error = false;
        if (!$this->product->validate()) {
            $error = true;
        }
        if (!$this->parcel->validate()) {
            $error = true;
        }
        if ($error) {
            $this->addError(null); // add an empty error to prevent saving
        }
        parent::afterValidate();
    }

    public function save()
    {
        if (!$this->validate()) {
            return false;
        }
        $transaction = Yii::$app->db->beginTransaction();
        if (!$this->product->save()) {
            $transaction->rollBack();
            return false;
        }
        $this->parcel->product_id = $this->product->id;
        if (!$this->parcel->save(false)) {
            $transaction->rollBack();
            return false;
        }
        $transaction->commit();
        return true;
    }

    public function getProduct()
    {
        return $this->_product;
    }

    public function setProduct($product)
    {
        if ($product instanceof Product) {
            $this->_product = $product;
        } else if (is_array($product)) {
            $this->_product->setAttributes($product);
        }
    }

    public function getParcel()
    {
        if ($this->_parcel === null) {
            if ($this->product->isNewRecord) {
                $this->_parcel = new Parcel();
                $this->_parcel->loadDefaultValues();
            } else {
                $this->_parcel = $this->product->parcel;
            }
        }
        return $this->_parcel;
    }

    public function setParcel($parcel)
    {
        if (is_array($parcel)) {
            $this->parcel->setAttributes($parcel);
        } elseif ($parcel instanceof Parcel) {
            $this->_parcel = $parcel;
        }
    }

    public function errorSummary($form)
    {
        $errorLists = [];
        foreach ($this->getAllModels() as $id => $model) {
            $errorList = $form->errorSummary($model, [
                'header' => '<p>Please fix the following errors for <b>' . $id . '</b></p>',
            ]);
            $errorList = str_replace('<li></li>', '', $errorList); // remove the empty error
            $errorLists[] = $errorList;
        }
        return implode('', $errorLists);
    }

    private function getAllModels()
    {
        return [
            'Product' => $this->product,
            'Parcel' => $this->parcel,
        ];
    }
}

CONTROLLER ACTIONS

The ProductForm class is used in actionUpdate() and actionCreate() methods in ProductController.
controllers/ProductController.php
<?php
namespace app\controllers;

use app\models\form\ProductForm;
use app\models\Product;
use Yii;
use yii\web\Controller;

class ProductController extends Controller
{
    public function actionCreate()
    {
        $productForm = new ProductForm();
        $productForm->product = new Product;
        $productForm->setAttributes(Yii::$app->request->post());
        if (Yii::$app->request->post() && $productForm->save()) {
            Yii::$app->getSession()->setFlash('success', 'Product has been created.');
            return $this->redirect(['update', 'id' => $productForm->product->id]);
        } elseif (!Yii::$app->request->isPost) {
            $productForm->load(Yii::$app->request->get());
        }
        return $this->render('create', ['productForm' => $productForm]);
    }
    
    public function actionUpdate($id)
    {
        $productForm = new ProductForm();
        $productForm->product = $this->findModel($id);
        $productForm->setAttributes(Yii::$app->request->post());
        if (Yii::$app->request->post() && $productForm->save()) {
            Yii::$app->getSession()->setFlash('success', 'Product has been updated.');
            return $this->redirect(['update', 'id' => $productForm->product->id]);
        } elseif (!Yii::$app->request->isPost) {
            $productForm->load(Yii::$app->request->get());
        }
        return $this->render('update', ['productForm' => $productForm]);
    }
    
    protected function findModel($id)
    {
        if (($model = Product::findOne($id)) !== null) {
            return $model;
        }
        throw new HttpException(404, 'The requested page does not exist.');
    }
}

FORM VIEWS

The views views/product/create.php and views/product/update.php will both render a form.
<?= $this->render('_form', ['productForm' => $productForm]); ?>
The form will have a section for the Product, and another section for the Parcels.
After saving, each Product and Parcel field will be validated individually, if any fail the error will be displayed at the top as well as on the errored field.
views/product/_form.php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;

?>
<div class="product-form">

    <?php $form = ActiveForm::begin(); ?>

    <?= $productForm->errorSummary($form); ?>

    <fieldset>
        <legend>Product</legend>
        <?= $form->field($productForm->product, 'name')->textInput() ?>
    </fieldset>

    <fieldset>
        <legend>Parcel</legend>
        <?= $form->field($productForm->parcel, 'code')->textInput() ?>
        <?= $form->field($productForm->parcel, 'width')->textInput() ?>
        <?= $form->field($productForm->parcel, 'height')->textInput() ?>
        <?= $form->field($productForm->parcel, 'depth')->textInput() ?>
    </fieldset>

    <?= Html::submitButton('Save'); ?>
    <?php ActiveForm::end(); ?>

</div>

ADDITIONAL INFORMATION

The $_POST data will look something like the following:
$_POST = [
    'Product' => [
        'name' => 'Keyboard and Mouse',
    ],
    'Parcel' => [
        'code' => 'keyboard',
        'width' => '50',
        'height' => '5',
        'depth' => '20',
    ],
];


Source: https://mrphp.com.au/blog/advanced-multi-model-forms-yii2/