20201227のPHPに関する記事は9件です。

Synfony2 メソッド,フォーム,アノテーション チートシート

個人的なメモであります

コントローラーメソッド

メソッド名 説明
generateUrl() ルート名からURLを生成
forward( ) 指定した名前のコントローラーへ処理を引き渡しその結果を取得
redirect( ) 指定したURLヘHTTPリダイレクトするレスポンスを生成
redirectToRoute()     リダイレクト先をルート名で指定してHTTPリダイレクトレスポンスを生成
addFlash( )      種類と文字列を指定してflashメッセージを登録
isGranted( ) 指定したロールを現在の認証ユーザが保持しているかどうかをチェック
denyAccessUnlessGranted( ) 指定したロールを現在の認証ユーザが保持していない場合アクセス拒否、レスポンスを例外としてスロー
renderView( ) テンプレートをレンダリングしその結果を取得
render( ) テンプレートをレンダリングして、それが格納されたResponseオブジェクトを取得
stream( ) ストリーム機能を使ってテンプレートを遅延レンダリングするResponseオブジェクトを取得する
createNotFoundException() Not Found のレスポンスを例外としてスロー
CreateAccessDeniedException( ) アクセス拒否レスポンスを例外としてスロー
createForm( ) FormTypeを指定してフォームオブジェクトを取得
createFormBuilder( ) FormBuilderオブジェクトを取得
getRequest() 現在のRequest オブジェクトを取得す
getDoctrine( ) Doctrine のレジストリオブジェクトを取得
getUser( ) 現在の認証ユーザオブジェクトを取得
has( ) サービスコンテナのhas( ) メソッドを呼び出す
get( ) サービスコンテナのget( ) メソッドを呼び出す
getParameter( ) サービスコンテナのgetParameter( )メソッドを呼び出す
IsCsrfTokenValid() CSRFトークンの有効性をチェック

Twigテンプレートメソッド

メソッド名 説明
autoescape ブロックに対して個別にエスケープを設定
block 継承関係にあるテンプレートとブロックを共有する
do {{...}}と同様に式の評価を行うが、出力はしない<
embed 他のテンプレートファイルを埋め込む(include の機能に加えてextendsのようにブロック単位で上書き可能
extends 指定したテンプレートファイルの継承を宣言
filter ブロックに対して任意のフィルタを適用
flush 出力バッファをフラッシュする
for ループ処理を実行する
form マクロを読み込む(import の別記法)
if 条件分岐を実行する
import マクロを読み込む
sandbox includeで読み込むテンプレートにサンドボックスモードを適用する
set 変数に値を代入する
include 指定したテンプレートファイルの内容を読み込む
macro マクロ(再利用可能なテンプレート部品)を定義する
spaceless HTMLタグ間の空白文字を削除(HTML内やテキスト内の空白文字ではない)
use extendsを使用しているテンプレートにさらに他のテンプレーファイルからブロックを読み込む
verbatim ブロック内をパースせず生テキストとして出力する

拡張タグ

メソッド名 説明
form_theme 指定したフォームに対してカスタマイズしたテーマをセットする
trans 翻訳を適用して返す
transchoice 選択肢に翻訳を適用して返す
trans_default_domain 翻訳に利用するデフォルトのドメインを設定する
stopwatch タグ内のコードの実行時間を計測してプロファイラに表示

Fromメソッド

メソッド名 説明
form フォーム全体をレンダリング
form_start フォームの開始タグをレンダリング
form_end フォームの終了タグをレンダリング
form_label フォームの個別フィールドのラベルをレンダリング
form_errors フォームのエラーをレンダリング
form_widget フォームの個別フィールドのウィジェットをレンダリング
form_row フォームの個別フィールド全体をレンダリング
form_rest フォームでまだレンダリングされていないフィールドをすべてレンダリング
form_enctype フォームのenctypeをレンダリング
render Controller 関数と合わせて使い、指定したコントローラの結果をレンダリング
render_esi renderと同様で、結果をESI機能で埋め込む
controller コントローラを指定
asset 指定したパスに対応する、アセットのパスを返す
asset_version アセットのバージョンを返す
csrf_token CSRFトークンを生成して返す
is_granted 認証ユーザのロールをチェック
logout_path 設定されたログアウトパスを返す

FormType

フォームタイプ 説明
text input type="text"
textarea input type="textarea"
email input type="email"
integer input type="integer"
password input type="password"
search input type="search"
percent テキスト入力と%記号
choice radio, checkbox, select
entity エンティティー覧の選択肢 (radio、checkbox、select)
kanguage 言語オード一覧 (radio、checkbox、select)
date 日付(単一テキスト、 複数テキスト、複数のselect、またはHTML5の日付ウィジェット)
datetime 日時(単一テキスト、 複数テキスト、複数のselect、またはHTML5の日付ウィジェット)
file ファイル(file)
collection コレクションを扱う場合に利用するフィールド
submit 送信ボタン(submit)

DoctrineのQueryBuilderメソッド

メソッド名 説明
expr( ) クエリパーツを組み立てるExprオブジェクトを取得
getType( ) 現在のクエリの種類を取得
getEntityManager( ) 関連づけられているエンティティマネージャを取得
getState( ) クエリビルダの変更状態を取得
getDQL( ) 現在のクエリビルダの内容からDQL文を取得
getQuery( ) 現在のクエリビルダの内容からクエリオブジェクトを取得
getRootAlias( ) クエリの起点となっているエンティティのエイリアスを取得
setParameter( ) クエリのパラメータを設定する
getParameter( ) 現在のクエリビルダに設定された特定のパラメータの値を取得
setMaxResults( ) 取得するレコードの件数を設定
getMaxResults( ) 取得するレコードの件数設定を取得
add( ) 種類を指定してクエリのパーツを追加
select( ) クエリで取得するエンティティを指定
distinct( ) DISTINCT(重複レコードを除外する)かどうかを設定
addSelect( ) クエリで取得するエンティティを追加
delete( ) DELETEクエリに設定
update( ) UPDATEクエリに設定
indexBy( ) コレクションのインデックスに利用するフィールドを指定して取得
join() クエリでJOINして取得するエンティティを指定
innerJoin( ) クエリでINNER JOINして取得するエンティティを指定
leftJoin( ) クエリでLEFT JOINして取得するエンティティを指定
set( ) 更新クエリで更新するフィールドと値を指定
where( ) クエリのWHERE句を指定
andWhere( ) クエリのWHERE句の条件をANDで追加
orWhere( ) クエリのWHERE句の条件をORで追加
groupBy( ) クエリの GROUP BY句の条件を指定
addGroupBy( ) クエリのGROUP BY句の条件を追加で指定
having( ) クエリのHAVING句の条件を指定
andHaving( ) クエリのHAVING句の条件をANDで追加
orderBy() クエリの結果の並び順を指定
addOrderBy( ) クエリの結果の並び順を追加で指定
getDQLpart( ) 現在のクエリビルダから指定した種類のDQLパーツを取得
resetDQLpart( ) 現在のクエリビルダから特定のDQLパーツをリセット

アノテーション

アノテーション 説明
@ORM\Column クラスのプロパティをDBテーブルのカラムとマッピング
@ORM\ColumnResult ネイティブSQLクエリの結果でスカラ値のマッピングを指定
@ORM\Cache キャッシュ方法を指定する
@ORM\ChangeTrackingPolicy エンティティの変更追跡方法を指定する
@ORM\DiscriminatorColumn Doctrine の継承を利用する場合の基底クラスで、サブクラスの識別名を保存するカラムを指定
@ORM\DiscriminatorMap Doctrine の継承を利用する場合の基底クラスで、サブクラスの識別名とエンティティクラス名との対応を指定
@ORM\Entity クラスをエンティティとして利用するよう指示
@ORM\EntityResult ネイティブSQLクエリの結果でエンティティへのマッピングを指定
@ORM\FieldResult ネイティブSQLクエリの結果でエンティティのフィールドへのマッピングを指定
@ORM\GeneratedValue エンティティのIDの生成方法を指定
@ORM\HasLifecycleCallbacks エンティティでライフサイクルコールバックを有効にする
@ORM\Index エンティティの対応するテーブルで、DBのインデックス定義を指定
@ORM\ld エンティティの識別子となるプロパティを指定
@ORM\InheritanceType Doctrine の継承を利用する場合、継承の種類を指定
@ORM\JoinColumn 関連を持ったエンティティの取得時にJOINを使う
@ORM\JoinColumns 複合キーの関連を持ったエンティティの取得時にJOINを使う
@ORM\JoinTable 関連エンティティを使った関連先をJOINして取得する
@ORM\ManyToOne 多対一の関連を指定
@ORM\ManyToMany 多対多の関連を指定
@ORM\MappedSuperclass Doctrine の継承を利用する場合、継承の親であることを指示
@ORM\NamedNativeQuery 名前付きのネイティブSQLクエリを定義
@ORM\OneToOne 一対一の関連を指定
@ORM\OneToMany 一対多の関連を指定
@ORM\OrderBy 多対多、一対多の関連先を取得するときの並び順を指定
@ORM\PostLoad ライフサイクルコールバックでエンティティを読み込んだあとに実行されるメソッドを指定
@ORM\PostPersist ライフサイクルコールバックでエンティティが初めてエンティティマネージャの管理下へ登録されたときに実行されるメソッドを指定
@ORM\PostRemove ライフサイクルコールバックでエンティティがエンティティマネージャから削除されたときに実行されるメソッドを指定
@ORM\PostUpdate ライフサイクルコールバックでエンティティの内容の更新が適用されたあとに実行されるメソッドを指定
@ORM\PrePersist ライフサイクルコールバックでエンティティが初めてエンティティマネージャの管理下へ登録される直前に実行されるメソッドを指定
@ORM\PreRemove ライフサイクルコールバックでエンティティがエンティティマネージャから削除される直前に実行されるメソッドを指定
@ORM\PreUpdate ライフサイクルコールバックで エンティティの内容の更新が適用される直前に実行されるメソッドを指定
@ORM\SequenceGenerator エンティティのIDの生成方法にSEQUENCEを指定している場合に利用するシーケンスを指定
@ORM\SqlResultSetMapping ネイティブSQLクエリの結果のマッピングを定義
@ORM\Table クラスをDBのテーブルとマッピング
@ORM\UniqueConstraint エンティティの一意性制約を指定
@ORM\Version ロックに使うバージョン値を保持するプロパティを指示
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel のwhereHasをwhereInに置き換える

追記

下記の方が、便利なライブラリを作成してくれています。
古のphpバージョンだと対応していない(未確認)ので、以降の記事は備忘録的な意味で残します。
https://qiita.com/mpyw/items/0761a5e44836c9bebcd5

難しいクエリを作りたくない

whereHasは遅い。激遅い。
left joinや、whereInすれば良いらしいと推測。
でもleft joinはコーディングがしんどいのよな……。

結論:whereInで頑張れば簡単だし改善はできるよ

modelの関係性

usersはcommentsと「1対多」の関係。

whereHasを用いた場合

前提

  • リレーション先のカラムでwhereしたい。
  • 姓と名が別々のカラムになっており、concatでlikeしないとならない。(この条件は今回関係ない)
UsersController.php
Comments::whereHas('Users', function($query) use ($user_name){
$query = $query->where(DB::raw('CONCAT(mei, sei)'), 'like', "%$user_name%");
});

whereInに置き換え

UsersController.php
// users単体で、クエリをたたく
$users = User::where(DB::raw('CONCAT(mei, sei)'), 'like', "%$user_name%")
            ->select('id')
            ->pluck('id')
            ->toArray();
// その結果を、当モデルに組み込む。
Comments::whereIn('user_id',$users);

計測はしていないけど、体感で明らかに早くなったのでよしとする。
生クエリや、left joinする方とどちらのコーディングが美しいかは言うな。
僕みたいなよわよわ頭脳の場合は上記の方が早く書けるし、見やすい!

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】PHPの論理演算子について

論理演算子とは?

論理演算子と言われてもピンとこないかもしれない。
ただ、それが何か言われると知っているし、使っていると思う。
ANDORのこと。
例があると非常に分かりやすいが、これらが論理演算子ということを覚えておきたい。

実際に使ってみる

//ANDを使する
$n = 40;
if($n >= 30 && $n <= 50) {
  echo($n)."は30以上かつ50以下です";
}else {
  echo($n)."は30以上かつ50以下ではありません";
}
//出力結果
40は30以上かつ50以下です

//ORを使用する
$n = 40;
if($n % 2 == 1 || $n >= 30) {
  echo($n)."は30以上もしくは奇数の数値です";
}else {
  echo($n)."は30より小さい偶数です";
}
//出力結果
40は30以上もしくは奇数の数値です

AND&&OR||と記述することが一般的?なのだろうか。

$n = 20;
if($n >= 30 AND $n <= 50) {
  echo($n)."は30以上かつ50以下です";
}else {
  echo($n)."は30以上かつ50以下ではありません";
}
//出力結果
40は30以上かつ50以下です

このようにANDと記述しても大丈夫だった。andも試してみたところ出力できた。
ORも同様にorでも出力はできていた。

やはり根本的な理解が私には欠けているのかもしれない。
もしくはこのような知識よりも何かアプリケーションを作成できる知識を重点的に学ぶべきなのか…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC-CUBE4 プラグインでマスターテーブルを追加する

はじめに

初めての投稿となります。
よろしくお願いします。

仕事で始めてEC-CUBE4を利用したECサイトを仕事で制作しました。
その際、プラグインでマスターテーブルを新規追加することになったのですが、
テーブルの新規追加はよく見かけるのに対し、マスターテーブルの記事が少ないように感じたので作成方法を簡単に記事にまとめよう思います。

新規プラグイン作成方法については多くのサイトが存在するのでそちらを参考にしください。

目標

マスターテーブル(mtb_sample)の新規追加。
管理画面>設定>システム設定>マスタデータ管理 へ登録。

最終的なマスターテーブル

形は以下のしたいと考えています。

id name sort_no discriminator_type
1 テスト1 1 samplemtbconfig
2 テスト2 2 samplemtbconfig
3 テスト3 3 samplemtbconfig
4 テスト4 4 samplemtbconfig

管理画面>設定>システム設定>マスタデータ管理

以下の設定にマスターテーブル(mtb_sample)を編集出来るようにします。
01_マスターデータ管理.png

EC-CUBE Version

 Version 4.0.4

プラグイン

プラグイン生成時の項目は以下にしています。

name:SamplePlugin
code:SamplePlugin
ver: 1.0.0

プラグインのディレクトリ構成

SamplePlugin\
|―― Entity\
  |―― Master\
    |―― SampleMtbConfig.php
|―― Repository\
  |―― Master\
    |―― SampleMtbRepository.php
|―― Form\
  |―― Type\
    |―― Extension\
      |―― SampleMasterdataType.php
|―― composer.json
|―― PluginManager.php

composer.json

プラグインの情報の記述

composer.json
{
  "name": "ec-cube/SamplePlugin",
  "version": "1.0.0",
  "description": "SamplePlugin",
  "type": "eccube-plugin",
  "require": {
    "ec-cube/plugin-installer": "~0.0.7"
  },
  "extra": {
    "code": "SamplePlugin"
  }
}

Entity\Master\SampleMtbConfig.php

まずはエンティティクラスを作成します。
普通のテーブルであればここに setId()やgetid()などを記述していくのですが、必要ありません。

以下の記述でマスターテーブルの名前を設定します。
@ORM\Table(name="mtb_sample")

SampleMtbConfig.php
<?php
namespace Plugin\SamplePlugin\Entity\Master;

use Doctrine\ORM\Mapping as ORM;

/**
 * SamplePlugin
 *
 * @ORM\Table(name="mtb_sample")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
 * @ORM\HasLifecycleCallbacks()
 * @ORM\Entity(repositoryClass="Plugin\SamplePlugin\Repository\Master\SampleMtbRepository")
 * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
 */
class SampleMtbConfig extends \Eccube\Entity\Master\AbstractMasterEntity
{
}

Repository\Master\SampleMtbRepository.php

Repositoryはテーブルのデータを操作する際に利用されます。

SampleMtbRepository.php
<?php
namespace Plugin\SamplePlugin\Repository\Master;

use Eccube\Repository\AbstractRepository;
use Plugin\SamplePlugin\Entity\Master\SampleMtbConfig;
use Symfony\Bridge\Doctrine\RegistryInterface;

class SampleMtbRepository extends AbstractRepository
{
    /**
     * 
     * @param RegistryInterface $registry
     */
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, SampleMtbConfig::class);
    }
}

Form\Extension\SampleMasterdataType.php

管理画面>設定>システム設定>マスタデータ管理 へ登録や変更を行うことが出来る機能へ反映させるための記述です。

SampleMasterdataType.php
<?php
namespace Plugin\SamplePlugin\Form\Type\Extension;

use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Eccube\Form\Type\Admin\MasterdataType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;


class SampleMasterdataType extends AbstractTypeExtension
{
    /**
     *
     * @var EntityManagerInterface
     */
    protected $entityManager;

    /**
     *
     * @param EntityManagerInterface $entityManager
     */
    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     *
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $masterdata = [];

        /** @var MappingDriverChain $driverChain */
        $driverChain = $this->entityManager->getConfiguration()->getMetadataDriverImpl();
        /** @var MappingDriver[] $drivers */
        $drivers = $driverChain->getDrivers();

        foreach ($drivers as $namespace => $driver) {
            if ($namespace == 'Eccube\Entity' || $namespace == 'Plugin\SamplePlugin\Entity') {
                $classNames = $driver->getAllClassNames();
                foreach ($classNames as $className) {
                    /** @var ClassMetadata $meta */
                    $meta = $this->entityManager->getMetadataFactory()->getMetadataFor($className);
                    if (strpos($meta->rootEntityName, 'Master') !== false
                        && $meta->hasField('id')
                        && $meta->hasField('name')
                        && $meta->hasField('sort_no')
                    ) {
                        $metadataName = str_replace('\\', '-', $meta->getName());
                        $masterdata[$metadataName] = $meta->getTableName();
                    }
                }
            }
        }

        $builder
            ->add('masterdata', ChoiceType::class, [
                'choices' => array_flip($masterdata),
                'expanded' => false,
                'multiple' => false,
                'constraints' => [
                    new Assert\NotBlank(),
                ],
            ])
            ;
    }

    /**
     *
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'admin_system_masterdata';
    }

    /**
     *
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return MasterdataType::class;
    }
}

PluginManager.php

PluginManager.phpはプラグインのインストールやアンインストール時に呼び出されますので、プラグインインストール時に、マスターデータを書き込む記述をします。

PluginManager.php
<?php
namespace Plugin\SamplePlugin;

use Eccube\Plugin\AbstractPluginManager;
use Plugin\SamplePlugin\Entity\Master\SampleMtbConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;


class PluginManager extends AbstractPluginManager
{
    /**
     *
     * @param array $meta
     * @param ContainerInterface $container
     */
    public function install(array $meta, ContainerInterface $container) {

        $em = $container->get('doctrine.orm.entity_manager');

        $CustomerQuestionnaire = new SampleMtbConfig();
        $CustomerQuestionnaire->setId('1');
        $CustomerQuestionnaire->setName('テスト1');
        $CustomerQuestionnaire->setSortNo('1');
        $em->persist($CustomerQuestionnaire);
        $em->flush();

        $CustomerQuestionnaire = new SampleMtbConfig();
        $CustomerQuestionnaire->setId('2');
        $CustomerQuestionnaire->setName('テスト2');
        $CustomerQuestionnaire->setSortNo('2');
        $em->persist($CustomerQuestionnaire);
        $em->flush();

        $CustomerQuestionnaire = new SampleMtbConfig();
        $CustomerQuestionnaire->setId('3');
        $CustomerQuestionnaire->setName('テスト3');
        $CustomerQuestionnaire->setSortNo('3');
        $em->persist($CustomerQuestionnaire);
        $em->flush();

        $CustomerQuestionnaire = new SampleMtbConfig();
        $CustomerQuestionnaire->setId('4');
        $CustomerQuestionnaire->setName('テスト4');
        $CustomerQuestionnaire->setSortNo('4');
        $em->persist($CustomerQuestionnaire);
        $em->flush();

    }

    /**
     *
     * @param array $meta
     * @param ContainerInterface $container
     */
    public function enable(array $meta, ContainerInterface $container) {
    }

    /**
     *
     * @param array $meta
     * @param ContainerInterface $container
     */
    public function disable(array $meta, ContainerInterface $container) {
    }
}

プラグインインストール

以上のコードを記述後、プラグインをインストールすれば(mtb_sample)が生成されます。
マスターデータ管理にも以下のように追加されて編集出来るようになりました。
03_マスターデータ管理.png

参考サイト

https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=22996&forum=10

最後に

拙い記事を最後まで見ていただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GCP Cloud Vision APIでドラゴンボール表紙をOCRにかけてみた

Cloud Vision API の 光学式文字認識(OCR)

Google Cloud Platform (GCP) > Cloud Vision API ではいろいろなものを検出できます。

先日 光学式文字認識(OCR) を利用してみて、精度の高さに驚きました。

そして、画像化した書籍は読めても、ではマンガはいけるのか、が気になっため、やってみました。


Cloud Vision API

https://cloud.google.com/vision

には 大きく、

  • AutoML Vision
  • Vision API

があり、

AutoML Vision はカスタムオブジェクトの検出などより高度なことができ、

Vision API は顔検出、ロゴ検出など、汎用的なオブジェクトの検出が手軽に利用できます。


今回は Vision API光学式文字認識(OCR) を利用しました。

解析した画像

ドラゴンボールコミックなどの表紙を解析しました。

左 = 元画像と解析結果を重ねたもの
右 = 解析結果のみを描画したもの

です。

ドラゴンボール 42 巻

画像

png_db42.png

読み取り内容

JUMP COMICS
DRAGON BALL
鳥山明
THANK YOU!
GOOD BYE !!
巻四十二
バイバイドラゴンワールド

著者名や、フキダシの THANK YOU! GOOD BYE !! まで拾っており、かなり正確に読み取りができてることがわかります。

ところが、青丸に黄文字の「ドラゴンボール」は、読めていないですね。

ドラゴンボール 13 巻

画像

png_db13.png

読み取り内容

JUMP COMICS
DRAC BALL
DRAGON
UNUK
CEAPSULE
NO. 158
鳥山明
卷十三系倍空の

ロゴ DORAGON は、イラストで隠れている文字を解釈するのは流石に難しいようでした。

人間には、 DORAGON がわかっているからそう脳内補完が働き、読める類のものですね。
(カスタムオブジェクト検知なら行けるかもです。未検証)

また、画像右、縦書き文字について、
孫悟空系倍空 になっていたり 逆襲 が読めていなかったりしていますが、
縦書きと横書きが混在していても、ある程度解釈できている事がわかります。

また、読み間違いについては、元画像の解像度を上げると改善するかも、という雰囲気を感じます。

ニンテンドークラシックミニ 週刊少年ジャンプ創刊50周年記念バージョン

コミックではありませんが、もっと複雑なものはどうだろうかと検証してみました。

画像

png_nump.png

読み取り内容

シテン
週刊少年ジャンプ
創刊50周年記念バージョン
ジャンプ特別編集
2018年8月号増刊
定価1080円(税込)
世界最クリアグ
ロ
ジャンプゲーム
特製ポスターつき
合黒神語。
スト
20本の
イ、
ファミコン ファミコン
ジャンプ ジャンプロ
英雄列伝
強のフ人。
★ファミコン神単を語る/★。
★英社の攻略本いろいろ★ジャン
■元週刊少年ジャンプ&
Vジャンプ編集長 島嶋和彦氏インタビュー
ビ k

これは流石に読み取りが難しかったようです。

思ったよりも、文字が読める

周囲とのコントラストが曖昧だったり、装飾的に過ぎたり、文字方向などが変則的なものはちょっと、という感じがしましたが、それでも十分読めてるな、と感じました。

フォントが違うものや、縦書き横書きが混在していてもOKなところは驚きです。

同様なもので、Google Drive で画像をアプリで開くと、同様に OCR にかけて文字を起こしてくれますが、
これを初めてやった 5 年前と比べると、格段に精度が上がったのではないかと感じますね。

これからもっと精度が上がっていくのではないかと感じます。
純粋にこういうのって面白いですね。

どうやったの ?

最後に、掲載した画像の作成手順についてさらっと記載します。

1.はじめに

Cloud Vision API > ドキュメント > ガイド > クイックスタート: クライアント ライブラリの使用
https://cloud.google.com/vision/docs/quickstart-client-libraries

のクライアントライブラリを導入し

2.次に

Cloud Vision API > ドキュメント > ガイド > 画像内のテキストを検出する
https://cloud.google.com/vision/docs/ocr

に掲載のあるプログラムを用意して画像解析のリクエストを行い

3.最後に

HTML の Canvas で、解析結果を描画した

という感じで組み立ててます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WordPressでOGPの自動出力をしてみた

はじめに

最近流行りのJamstack、いいですよね。
弊社でもGatsby.jsを使ったJamstackの実績があります。(※担当エンジニアは私じゃないです)
“TINDER” CORPORATE WEB SITE & MOVIE|WORKS|PLUS-D Inc.

Gatsby.jsを調べているうちに、Gatsby.jsでOGPの自動出力をしたという記事を拝見しました。
GatsbyJSでOGP画像の動的生成にチャレンジしてみた | Learn Something New

で、OGP自動出力したすぎる、と。目的がGatsby.jsからOPG自動出力に変わりまして。
じゃあ、個人でやってるブログで試したろ!と思ったのですが、私のブログ、ヘッドレスCMS化?SSR?なにそれ?みたいなWordpressを使ったフツーのブログなんですよね。(しかも1年以上放置)

え、サイト作り直すのだる……PHPでどうにかできるならしよ。

というのが、今回の趣旨です。

PHPで画像作成

そもそもできるのか、というところから調べたのですが、できるらしいです。
imgタグには普通画像を指定しますが、.phpファイルを指定できるらしい。
PHPってサーバー関係!みたいなザックリした理解の仕方をしていたのでなんか画像と結びついてなかった。
そして驚きのあまりこの記事を書いているんですが、そんなの知ってるよって方はもう話すことはないです。

参考:PHPで画像を動的に生成する【GD編】 | バシャログ。

ディレクトリ構成

ファイルを置く場所は極端にいえばドキュメントルートでもテーマ内でもどこでもいいのですが、こんな感じで。

/og
├─img.php
├─og_base.png
└─xxx.ttf

.phpファイル、画像、フォントが必要です。

画像はなくても作れますが、今回はこちらを用意しました。こちらの上にテキストを重ねるだけの簡単なOGPを作ります。
ogp.png

PHPを書く

img.php
<?php
  $fontSize = 40; // 文字サイズ
  $fontFamily = 'xxx.ttf'; // 字体
  $txtX = $fontSize; // 文字の横位置(文字の左が基準)
  $txtY = $fontSize * 2; // 文字の縦位置(文字のベースラインが基準)
  $txt = $_GET['text']; // テキスト

  $img = imagecreatefrompng('ogp_base.png'); // テキストを載せる画像
  // $img = imagecreatefromjpg('ogp_base.jpg'); // 元画像がjpgの場合はこうなります
  $color = imagecolorallocate($img, 0, 0, 0); // テキストの色指定(RGB)

  imagettftext($img, $fontSize, 0, $txtX, $txtY, $color, $fontFamily, $txt);
  header('Content-Type: image/png');
  imagepng($img);
  imagedestroy($img);

上はいろいろ指定なので、下4行が重要です。
一つだけ注意が必要なのが縦位置。文字のベースラインが基準となっているため、縦位置に0を指定するとテキストが画像の外に行ってしまいます。
いい感じの改行などはできないので実際はもう少しいじる必要があります。

imagettftext($img, $fontSize, 0, $txtX, $txtY, $color, $fontFamily, $txt);
重ねるテキストに関する指定です。左から順番に下記を指定します。

  • 重ねる画像
  • 文字サイズ
  • テキストの角度
  • 横位置
  • 縦位置
  • フォント
  • テキスト

header('Content-Type: image/png');
画像として情報を送信します。

imagepng($img);
PNG画像を表示。

imagedestroy($img);
メモリを解放。

og:imageを指定

header.phpなど
<meta property="og:image" content="img.php?text=<?php the_title(); ?>">

PHPファイル内で取得するテキストデータに合わせてパラメータを付けます。

できた

たとえば、同じ内容をアップしている下記記事だと、こんな感じです。アッ、ブログはメモとして使っていてもっと雑に簡略化して書いてます。よろしくお願いします。
WordPressでOGPの自動出力をする方法 | I log.
createimg.png
JKゴシックかわいい。

まとめ

  • PHPで画像生成ができる
  • imgタグやmetaタグなど、画像を指定するときに.phpファイルを指定できる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel すべてのviewで共通する変数の設定

はじめに

Laravelを勉強中です。備忘録として残しておきます。

手順

サービスプロバイダーの作成

app/Provider/ComposerServiceProvider.php
<?php
namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Auth;

class ComposerServiceProvider extends ServiceProvider
{
    public function boot()
    {
        View::composer('*', function($view) {
            $view->with('user'(使いたい変数名), Auth::user()(使いたいデータ));
        });
    }
}

View::composerの第一引数「*」はすべてのviewという意味。

作成したComposerServiceProviderの登録

config/app.php
    'providers' => [
        .
        .
        .
     App\Providers\ComposerServiceProvider::class,
    ],

これですべてのviewで使用できるようになります。

参考にさせて頂いたサイト

https://www.webopixel.net/php/1287.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[仕事納め]2020年にやらかしたミスをまとめる[未経験から2年目]

こんにちは。web系を中心としたプラグラマをしております。
小さなミスからそこそこ大きめのミスまで、今年1年でたくさんのミスをしてきました。
まだ新人で大きな権限を頂いていないこともあり、本番環境でやらかしてしまった皆様ほどのインパクトあるやらかしはないのですが、コーディングや開発環境構築などであれこれ反省点があるのでまとめていきたいと思います。
アンチパターンやあるあるとしてお楽しみ下さい。

コーディング系
環境系
人として

コーディング系

誤字

とにかく多かったです。
クラス名や関数名ならすぐエラーが発生して見つけやすいのですが、phpで変数名のスペルミスをしたりすると、そのままnullが代入されたりして見つけづらく、時間の無駄になりがちでした。

$address = 'hogehoge';
print $adress; // 返却値なし

くだらない誤字ですが、この手のエラーが出ない誤字で無駄にした時間も、年間を通せばかなり膨大になって来ます。
解決策としては、コードを細かく切り分けてこまめにテストを実行しました。これによって誤字箇所を発見するのが早くなった実感があります。
また、エディタはVSCodeを使用しており補完機能は元々使っていたのですが、コード量が多いとなかなか読み込まず使用を諦めて手入力することもあったので、今後補完機能をうまく活用できるように工夫していきたいです。
......そんなことより静的言語を使った方がいいのかもしれません。

エスケープ

先日LaravelselectRawメソッドを用いて生に近いSQLを書く機会がありました。WHERE句の対象となるカラムにバックスラッシュを用いたパスが入っていて、この部分をエスケープ出来ておらず検索結果が0となる失敗を犯しました。この質問と大体同じ内容です。

// 失敗例
DB::table('hoge')
    ->selectRaw('* WHERE `path`="PATH\\TO\\SOME\\MODEL"')
    ->get();

一見エスケープ出来ていそうですが、phpではダブルクォート内ではバックスラッシュが ///となり、かつ、Laravelのraw関係のメソッドではエスケープされない(というかrawって書いてあるのでそりゃそうですね)ので、MySQLに送られるクエリは以下のようになります。

SELECT * FROM hoge WHERE `path`='PATH\TO\SOME\MODEL' 

結果、MySQLでもエスケープされて無事死亡しました。
できるだけこのような生っぽいSQLは書かないのが一番ですが、この時は諸事情で仕方なかったので、以下のようにして解決しました。

// 成功例
DB::table('hoge')
    ->selectRaw('* WHERE `path`="PATH\\\\TO\\\\SOME\\\\MODEL"')
    ->get();
SELECT * FROM hoge WHERE `path`='PATH\\TO\\SOME\\MODEL' 

ともかく、何かとバックスラッシュは問題のタネになりやすいので警戒していきたいと思います。

環境系

Dockerコンテナの内か外か

Dockerを立ち上げてシェル作業する時、必要なコマンドをコンテナの内側で行うのか外側で行うのかを間違えて command not found になったり、最悪の場合想定外の処理が始まったりします(しました)。
基本的にどのコマンドをどこで実行するのかはプロジェクトによるので、実行前に確認するようになりました。特にcomposerの実行場所で事故ったので気をつけていきたいと思います。

バージョン違い

composerで依存パッケージをインストールすることが多いのですが、マイナーバージョンは指定しておらず不具合が発生する場合があります。
また、私が今年特に詰まったのはcomposer自体のバージョン違いです。
2020年10月頃にcomposerのバージョン2.0がリリースされました。これに気づかずdocker-composeコマンドを実行してしまった結果、DockerFile内にcomposer関係の諸々のコマンドが実行されてしまい、大量のエラーが発生しました。
この件の解決策は、こちらの方と同じ方法を使いました。
コマンドは以下のものです。

curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --1 --filename=composer

バージョンアップの詳細はこちらです。
        

人として

連絡が遅い

思い出すと胃が痛くなります.......
進捗が悪いのに、頑張って挽回しようと試行錯誤していて締め切りギリギリまで上司への進捗が遅れている旨の連絡をしませんでした。上司からは特に怒られず、淡々と「了解です。次は早めに連絡・相談して下さい」という内容のメッセージが返ってきて逆に怖かったです。
とにかく、早めの報告・連絡・相談は本当に大切です。技術的なミスより何より連絡不足が一番信用を失うと思いました。

仕様通りでない

仕様変更が頻繁でも、仕様書が読みづらくても、決定した仕様が一意である以上、仕様通りでないのは絶対にダメですよね。
コミット前にもう一度、成果物のチェックを怠らない。大事だと思います。
あと、最初に仕様書を通しで読んだ時に矛盾点を見つけられるようになりたいです。実装を始めてから気づくことが多いので.......

まとめ

結局、ほとんどのミスが確認不足に起因していました。
コミット前、コマンド入力前、上司への報告前......この記事を振り返ってミスを減らしていきたいと思います。
というわけで、皆様1年間お疲れ様でした。2021年も頑張っていきましょう!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP 月末の日にちを取得する

目的

  • PHPで月末の日にちを取得する方法をメモ的にまとめる

前提情報

  • 下記サービスを用いてWeb上でコードの動作を確認した。
  • 忘れそうなのでメモ的にまとめる。

詳細

  1. ひと月前の月末の日にちを取得したい時は下記を実行する。(YYYY/MM/DDの形式で情報を取得する事ができる。)

    $last_day_of_last_month = date('Y/m/d', mktime(0, 0, 0, date('m'), 0, date('Y')));
    echo $last_day_of_last_month;
    
  2. 今月の月末の日にちを取得したい時は下記を実行する。(YYYY/MM/DDの形式で情報を取得する事ができる。)

    $last_day_of_this_month = date('Y/m/d', mktime(0, 0, 0, date('m') + 1, 0, date('Y')));
    echo $last_day_of_this_month;
    
  3. 来月の月末の日にちを取得したい時は下記を実行する。(YYYY/MM/DDの形式で情報を取得する事ができる。)

    $last_day_of_next_month = date('Y/m/d', mktime(0, 0, 0, date('m') + 1, 0, date('Y')));
    echo $last_day_of_next_month;
    

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む