Rodhos Soft

備忘録を兼ねた技術的なメモです。Rofhos SoftではiOSアプリ開発を中心としてAndroid, Webサービス等の開発を承っております。まずはご相談下さい。

コンポーネント

コントローラで使える部品。

設定が必要なコンポーネントを使う際の設定

class HogesController extends AppController
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Auth', [
            'authorize' => 'Controller',
            'loginAction' => ['controller' => 'Users', 'action' => 'login']
        ]);

        $this->loadComponent('Cookie', ['expires' => '1 day']);
    }

}

Controller->beforeFilter()でconfig()を使って設定する方法もあり。
設定は$this->Auth->config('loginAction');でよみ取れる。

コンポーネントの使用

        if ($this->Post->delete($this->request->getData('Post.id')) {

要するに移譲している。

コンポーネントの実装

namespace App\Controller\Component;

use Cake\Controller\Component;

class MathComponent extends Component
{
    public function doComplexOperation($amount1, $amount2)
    {
        return $amount1 + $amount2;
    }
}

コンポーネントから他のコンポーネントを使う。

   public $components = ['Existing'];

これで$this->Existingとなってコンポーネントから色々使える。

コンポーネントからコントローラにアクセス。。orz
$controller = $this->_registry->getController();
$controller = $event->getSubject();
コールバック
  1. beforeFilter(Event $event)
  2. startup(Event $event)
  3. beforeRender(Event $event)
  4. shutdown(Event $event)
  5. beforeRedirect(Event $event, $url, Response $response)

イベントシステム

これは
イベントシステム - 3.x
の勉強メモである。


オブザーバーパターンの利用
イベントオブジェクトはすべてのリスナーに行き渡る。
そしてすべてのビュー、コントローラ、テーブルで
$events = $this->eventManager();
がある。。各モデルは独立したイベントマネージャーを持つ。

イベントの通知(dispatch)

// Cart/Model/Table/HogesTable.php
namespace Cart\Model\Table;

use Cake\Event\Event;
use Cake\ORM\Table;

class HogesTable extends Table
{

    public function place($order)
    {
        if ($this->save($order)) {
            $this->Cart->remove($order);
// イベント作成
            $event = new Event('Model.Order.afterPlace', $this, [
                'order' => $order
            ]);
// 通知
            $this->eventManager()->dispatch($event);
            return true;
        }
        return false;
    }
}

グローバルイベントマネージャーはアプリ内で起こる任意のイベントを受け取れる。
Cake\Event\EventManagerのシングルトン

// イベントの前に実行される任意の設定ファイルやコードの一部の中で
use Cake\Event\EventManager;

EventManager::instance()->on(
    'Model.Order.afterPlace',
    $aCallback
);

イベントの追跡

EventManager::instance()->setEventList(new EventList());

これで何を通知していったかわかる。

$eventsFired = EventManager::instance()->getEventList();
$firstEvent = $eventsFired[0];

コアイベント

ORM/Model イベント

テーブルオブジェクト - 3.x

Model.initialize
Model.beforeMarshal
Model.beforeFind
Model.buildValidator
Model.buildRules
Model.beforeRules
Model.afterRules
Model.beforeSave
Model.afterSave
Model.afterSaveCommit
Model.beforeDelete
Model.afterDelete
Model.afterDeleteCommit

コントローラーイベント

コントローラー - 3.x

Controller.initialize
Controller.startup
Controller.beforeRedirect
Controller.beforeRender
Controller.shutdown

ビューイベント

ビュー - 3.x

View.beforeRender
View.beforeRenderFile
View.afterRenderFile
View.afterRender
View.beforeLayout
View.afterLayout

リスナーの追加方法

Cake\Event\EventListenerInterfaceを実装するすなわち
implementedEvents()を実装する。

use Cake\Event\EventListenerInterface;

class Hoge implements EventListenerInterface
{
// イベントごとにどのメソッドを呼ぶか分ける
    public function implementedEvents()
    {
        return [
            'Model.Order.afterPlace' => 'updateBuyStatistic',
        ];
    }
// 各イベントの対処
    public function updateBuyStatistic($event, $order)
    {
        // 統計値を更新するコード
    }
}

// 登録
// Hoge オブジェクトを Order のイベントマネージャーに追加
$hoge = new Hoge();
$this->Orders->eventManager()->on($hoge);
無名リスナーの登録

ブロックで直接登録してしまう。

use Cake\Log\Log;

$this->Orders->eventManager()->on('Model.Order.afterPlace', function ($event) {
    Log::write(
        'info',
        'A new order was placed with id: ' . $event->getSubject()->id
    );
});
リスナーがいるかのチェック

リスナー登録が

// EventManager にリスナーを追加
$this->eventManager()->on('Hoge.Registration', [$this, 'hogeRegistration']);
$this->eventManager()->on('Hoge.Verification', [$this, 'hogeVerification']);
$this->eventManager()->on('Hoge.Authorization', [$this, 'hogeAuthorization']);

として

// アプリケーションのどこか別の場所で
$events = $this->eventManager()->matchingListeners('Verification');
if (!empty($events)) {
    $this->eventManager()->off('Hoge.Verification');
} else {
    // 'Verification' イベントリスナーが存在しない場合のロジックを実行。
}

リスナーには呼び出し順にpriorityをつけることもできる。勉強中

イベントデータ
$this->eventManager()
    ->dispatch(new Event('View.afterRender', $this, ['view' => $viewFileName]));

の場合、afterRenderのコールバックのリスナー(実装メソッドは第2引数で$viewFileNameを受け取る。

function (Event $event, $viewFileName)
イベントの送信
// 第1引数はイベント名
// 第2引数はsubject(イベントに関連づけるオブジェクト(通常自分)
// 第3引数はイベントパラメータで辞書がおすすめ
$event = new Event('Model.Order.afterPlace', $this, [
    'order' => $order
]);
$this->eventManager()->dispatch($event);
イベントの中止(後続のイベントコールバックを防ぐ)

通知を受けたメソッドがfalseをreturnするか
$event->stopPropagation();
を呼べば良い。
ストップすると$event->isStopped()がtrueになる。

イベント結果を返す。
// リスナーコールバック
public function doSomething($event)
{
    // .結果としてalteredDataを作って結果を返す
    $alteredData = $event->getData('order') + $moreData;
    return $alteredData;
}

// 別のリスナーコールバック
public function doSomethingElse($event)
{
    // returnで返す以外にsetResultというのでも返せる。
    $event->setResult(['order' => $alteredData] + $this->result());
}

// イベントを通知してその結果を得る
public function place($order)
{
    $event = new Event('Model.Order.beforePlace', $this, ['order' => $order]);

//  配信
    $this->eventManager()->dispatch($event);

// 配信結果をうけとる
    if (!empty($event->getResult()['order'])) {
        $order = $event->getResult()['order'];
    }
    if ($this->Orders->save($order)) {
        // ...
    }
    // ...
}

*** リスナーの削除

Cake\Event\EventManager::off()

イベントの注意

イベントは便利だが無思慮に使うとデバッグが困難になるので注意。

View

以下を参照して勉強したメモ
ビュー - 3.x

役割

HTML, XML, JSON, PDF生成等
XMLJSONビューは
JSON と XML ビュー - 3.x

AppView アプリのベースのView

主にヘルパーを読み込むように使用する。

<?php
namespace App\View;

use Cake\View\View;

class AppView extends View
{

    public function initialize()
    {
        $this->loadHelper('MyUtils');
    }

}

テンプレート ctp

両者は同じ意味

<?php echo $variable; ?>
<?= $variable ?>

テンプレート内での制御構文 (やりたくない。。)
if 、 for 、 foreach 、 switch 、そして while

<ul>
<?php foreach ($todo as $item): ?>
  <li><?= $item ?></li>
<?php endforeach; ?>
</ul>

このように":"で書いて、end***で閉じる。

ifの例

<?php if ($username === 'a'): ?>
   <h3>a</h3>
<?php elseif ($username === 'b'): ?>
   <h3>b</h3>
<?php else: ?>
   <h3>unknown</h3>
<?php endif; ?>

ビューレイヤーのパーツ

  1. ビュー(テンプレート)
  2. エレメント 描画時にビューのテンプレートに埋め込まれる
  3. レイアウト ビューのテンプレートを包む
  4. ヘルパー ビューロジックのカプセル化
  5. cells   自己完結型のUI部品

ビューへコントローラから値を渡す

コントローラのset

エスケープ

h()

ビューからレイアウトやエレメントに値を渡す

ビューのset

$this->set('activeMenuButton', 'posts');

ビューのテンプレートを継承したい。

$this->extendを用いる。入れ子にできる。これとビューブロックを用いて親テンプレートと子テンプレートをつくり継承関係をつくる。

ビューブロック

使用メソッド
start() 、 append() 、 prepend() 、 assign() 、 fetch() 、 そして end()

サイドバーブロックを定義する。

// sidebar ブロックを作成する。
$this->start('sidebar');
echo $this->element('sidebar/recent_topics');
echo $this->element('sidebar/recent_comments');
$this->end();

これでサイドバーブロックが定義されたので、
使う場合、

<?= $this->fetch('sidebar') ?>

とすれば使えるようになる。

ビューブロックが定義されていない場合も考慮する場合

<?php if ($this->fetch('sidebar')): ?>
<?= $this->fetch('sidebar') ?>
<?php endif; ?>

fetchの第2引数を定義すると、ビューブロックがない場合の既定値にできる。

    <?= $this->fetch('sidebar', 'サイドバーがない') ?>

ビューテンプレートの継承

親 src/Template/Common/view.ctp

<h1><?= $this->fetch('title') ?></h1>
<?= $this->fetch('content') ?>

<div class="actions">
    <h3>サイドバー</h3>
    <ul>
    <?= $this->fetch('sidebar') ?>
    </ul>
</div>

子  src/Template/Posts/view.ctp

<?php
// 親のビューを継承する
$this->extend('/Common/view');

// 親の`title`に$postの値を入れる。
$this->assign('title', $post);

// 親のサイドバーの部分を定義
$this->start('sidebar');
?>
<li>
<?php
echo $this->Html->link('edit', [
    'action' => 'edit',
    $post->id
]); ?>
</li>
<?php $this->end(); ?>

// 他はすべて親のcontetのところになる。(contentは特殊)
<?= h($post->body) ?>

ビューブロックの列挙

$list = $this->blocks();

ビューブロックの詳細

既存ブロックへの追加

$this->append('sidebar');
echo $this->element('sidebar/popular_topics');
$this->end();
$this->append('sidebar', $this->element('sidebar/popular_topics'));

既存ブロックの手前に追加

$this->prepend('sidebar', 'このコンテンツはサイドバーの先頭に来ます');

ブロックの消去

$this->reset('sidebar');
// or 
$this->assign('sidebar', '');

レイアウト

デフォルトレイアウト
src/Template/Layout/default.ctp

<!DOCTYPE html>
<html lang="en">
<head>
<title><?= h($this->fetch('title')) ?></title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<!-- 外部ファイルとスクリプトファイルがここに入れます (詳しくは HTML ヘルパーを参照。) -->
<?php
echo $this->fetch('meta');
echo $this->fetch('css');
echo $this->fetch('script');
?>
</head>
<body>

<!-- もしすべてのビューでメニューを表示したい場合、ここに入れます -->
<div id="header">
    <div id="menu">...</div>
</div>

<!-- ここがビューで表示されるようにしたい場所です -->
<?= $this->fetch('content') ?>

<!-- 表示される各ページにフッターを追加します -->
<div id="footer">...</div>

</body>
</html>

fetchが使われているので子で色々値を入れたりすることがわかる(ビューの継承と同じ)。

デフォルトレイアウトからの変更はControler->viewBuilder()->setLayout

3.4以前は違うらしい。

ビューからも変えられる? View->layout

エレメント

エレメントをテンプレートに埋め込む

echo $this->element('helpbox');

データを渡す。

echo $this->element('helpbox', [
    "helptext" => "hoge!"
]);

エレメントはキャッシュとコールバックを定義できる。

echo $this->element('helpbox', [
        "helptext" => "help!",
        "foobar" => "hogehoge",
    ],
    [
        "cache" => true,
        // エレメントから before/afterRender が呼び出されるには true に設定してください
        "callbacks" => true
    ]
);

キャッシュの詳細は
ビュー - 3.x
をみよ。

しかし、callbackとかでロジックがいるようならビューセルを検討する。

ビューイベント

View.beforeRender
View.beforeRenderFile
View.afterRenderFile
View.afterRender
View.beforeLayout
View.afterLayout

イベントリスナーを登録する?
EventListenerInterface
勉強中
イベントシステム - 3.x

独自のビューロジックを持つビューをつくる

src/Viewに配置、参照の際はViewを除いた部分。

// src/View/HogeView.php の中で
namespace App\View;

use Cake\View\View;

class HogeView extends View
{
    public function render($view = null, $layout = null)
    {
        // カスタムロジック
    }
}

ビューセル

ビューセル - 3.x

src/Template/Cell/に置く。

namespace App\View\Cell;

use Cake\View\Cell;

class HogeCell extends Cell
{

    public function display()
    {
    }

}

bakeでも作れる

bin/cake bake cell Hoge

未読メッセージの数を出す。

namespace App\View\Cell;

use Cake\View\Cell;

class HogeCell extends Cell
{

    public function display()
    {
        $this->loadModel('Messages');
        $unread = $this->Messages->find('unread');
        $this->set('unread_count', $unread->count());
    }

   public function other()
  {
  }
}

ポイントはcellはコントローラと同じようにloadModelでき、setでセルテンプレートにsetできること。
セルテンプレート側では

<!-- src/Template/Cell/Hoge/display.ctp -->
<div class="notification-icon">
    未読メッセージが <?= $unread_count ?> 件あります。
</div>

このあたりもコントローラとの関係と近い。

そのセルをビューテンプレートで用いる

$cell = $this->cell('Hoge');

render() ?>
cellをechoすると__toString()を使用するために意味のあるエラーメッセージを読み取れなくなるらしいのでrenderする。

// HogeCellの他のメソッドを呼び出す

$cell = $this->cell('Hoge::other');

値をcellに渡す。

$cell = $this->cell('Hoge::other', ['ok']);

セル側は

   public function other($okString)
  {
  }


異なるセルテンプレートを用いる。

echo $this->cell('Hoge::other')->render('messages');

もしくは

$cell = $this->cell('Hoge::other');
$cell->template = 'messages';
echo $cell;

セルはキャッシュもできる。
ビューセル - 3.x

$cell = $this->cell('Hoge', [], ['cache' => true]);

コントローラからセルを使いたい

namespace App\Controller;

use App\Controller\AppController;
use Cake\View\CellTrait;

class FooController extends AppController
{
    use CellTrait;

    // 他のコード。
}

ヘルパー

以下を参照している。
ヘルパー - 3.x

使うヘルパーはビューでロードする。

class AppView extends View
{
    public function initialize()
    {
        parent::initialize();
        $this->loadHelper('Html');
        $this->loadHelper('Form');
        $this->loadHelper('Flash');
    }
}

CakePHPにビルトインされているヘルパーはロードしなくても使える。

条件付きでヘルパーを読み込みたい

class AppView extends View
{
    public function initialize()
    {
        parent::initialize();
        if ($this->request->getParam('action') === 'index') {
            $this->loadHelper('ListPage');
        }
    }
}

コントローラ側で読み込んでおきたいならビュービルダーで

class HogesController extends AppController
{
    public function beforeRender(Event $event)
    {
        parent::beforeRender($event);
        $this->viewBuilder()->helpers(['MyHelper']);
    }
}

ヘルパーにオプションを渡す。
3.2以降

namespace App\View\Helper;

use Cake\View\Helper;
use Cake\View\View;

class HogeHelper extends Helper
{
    public function initialize(array $config)
    {
        debug($config);
    }
}

渡す方は

namespace App\Controller;

use App\Controller\AppController;

class HogesController extends AppController
{
    public $helpers = ['Hoge' => ['option1' => 'value1']];
}

等としてやる。

オプションのデフォルト値は必ず定義がいる。

namespace App\View\Helper;

use Cake\View\Helper;
use Cake\View\StringTemplateTrait;

class HogeHelper extends Helper
{

    use StringTemplateTrait;

    protected $_defaultConfig = [
        'option1' => 'o',
    ];
}

設定の読み取り

$option1= $this->Hoge->config('option1');

ヘルパーの別名
勉強中

ヘルパーの使用
echo $this->Hoge->foo('styles');
既存ヘルパーにメソッド追加
/* src/View/Helper/HogeHelper.php (他のヘルパーを使用) */

namespace App\View\Helper;

use Cake\View\Helper;

class HogeHelper extends Helper
{
    public $helpers = ['Html'];

    public function makeEdit($title, $url)
    {
        // 出力に HTML ヘルパーを使用
        // 整形されたデータ:

        $link = $this->Html->link($title, $url, ['class' => 'edit']);

        return '<div class="editOuter">' . $link . '</div>';
    }
}
ヘルパー内からビューの変数にアクセスできる。。。

$this->_View->get()

class HogeHelper extends Helper
{

    public $helpers = ['Html'];

    public someMethod()
    {
        // meta description の設定
        echo $this->Html->meta(
            'description', $this->_View->get('metaDescription'), ['block' => 'meta']
        );
    }
}

エレメントも当然できる。
$this->_View->element()

ヘルパーにある各種コールバック
  1. Helper::beforeRenderFile(Event $event, $viewFile)
  2. Helper::afterRenderFile(Event $event, $viewFile, $content)
  3. Helper::beforeRender(Event $event, $viewFile)
  4. Helper::afterRender(Event $event, $viewFile)
  5. Helper::beforeLayout(Event $event, $layoutFile)
  6. Helper::afterLayout(Event $event, $layoutFile)

テーマ

勉強中

HelloWorld 3回目

モデルと

Helloコントローラ
<?php
namespace App\Controller;

use App\Controller\AppController;

class HelloController extends AppController {
    public $autoRender = true;
    //public $name = "Hello";


    public function index()
    {
        /// Hogesモデルを利用
        $this->loadModel('Hoges');

//        echo "hello world!!";
    //src/Template/Layout/default.ctp レイアウトの使用
        $this->viewBuilder()->autoLayout(true);
        $this->ViewBuilder()->layout('Hello'); //Hello.ctpつかう。

// セットした値はテンプレート側で$poi等で見える。
        $this->set('poi',100);
        $this->set('poi2',200);
        $this->set('poi3',"hoge");

        // Hogesテーブルのすべてをとってくる
        $query = $this->Hoges->find(); // { "id": 1, "setting": 112121 }配列
        $this->set('hoges',$query);

        
    }

    public function other()
    {
        echo "hello other world!!";
    }
}
?>
Helloテンプレ

デフォルトレイアウトが適用されている。

    <h1>midashi</h1>
    <p> hello hello </p>
    <?= $poi ?>
    <?= $poi2 ?>
    <?= $poi3 ?>
    <?= $hoges ?>


    <?php foreach ($hoges as $obj): ?>
    <?= $obj ?>
    <?php endforeach; ?>

<!-- ヘルパーを使ってテーブルで表示 -->
<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>SETTING</th>
    </tr>
  </thead>
  <?php
  $arr = $hoges->toArray();// 配列に取り出す
  for ($i = 0 ; $i < count($arr); $i++) {
    echo $this->Html->tableCells(
      $arr[$i]->toArray(),
      ['style' => 'background-color:#f0f0f0'],
      ['style' => 'font-weight:bold'],
      true);
  }
    ?>
  </table>


  <?= $this->element('fooelement', ['va'=>$hoges]) ?>

エレメント
<!-- $vaを必要とする? -->
<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>SETTING</th>
    </tr>
  </thead>
  <?php
  $arr = $va->toArray();// 配列に取り出す
  for ($i = 0 ; $i < count($arr); $i++) {
    echo $this->Html->tableCells(
      $arr[$i]->toArray(),
      ['style' => 'background-color:#f0f0f0'],
      ['style' => 'font-weight:bold'],
      true);
  }
    ?>
  </table>
*** Hogesテーブル(bakeで作った)
>|php|
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Hoges Model
 *
 * @method \App\Model\Entity\Hoge get($primaryKey, $options = [])
 * @method \App\Model\Entity\Hoge newEntity($data = null, array $options = [])
 * @method \App\Model\Entity\Hoge[] newEntities(array $data, array $options = [])
 * @method \App\Model\Entity\Hoge|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
 * @method \App\Model\Entity\Hoge patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
 * @method \App\Model\Entity\Hoge[] patchEntities($entities, array $data, array $options = [])
 * @method \App\Model\Entity\Hoge findOrCreate($search, callable $callback = null, $options = [])
 */
class HogesTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('hoges');
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->integer('setting')
            ->allowEmpty('setting');

        return $validator;
    }
}

画像をUIImageに

    UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, [[UIScreen mainScreen] scale]);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, 0);
    CGContextScaleCTM(context, 1.0, 1.0);
    [self.layer renderInContext:context];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;

背景を塗っておかないと変になる。

CakePHPの勉強のまとめ

勉強ログの記事一覧

パッケージ管理

MacPorts, HomeBrew, Fink等の選択肢がある。

以下を参考にした。
MacPorts と Homebrew の違いについて

とりあえずHomeBrewをつかう。

brew.sh

brewコマンドが使えるようになる。

graphvizのインストー

brew install graphviz

FaceBookPHP

以下を参照した。
Facebook PHPをComposerで利用するには | hrendoh's memo


comporser.jsonのrequireにfacebook/php-sdkを追加

curl -s http://getcomposer.org/installer | php

でcomposer.pharを落として

 php composer.phar install

警告が出た。

Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.

updateしてみる。

php composer.phar update


次の警告が出た。

Package facebook/php-sdk is abandoned, you should avoid using it. Use facebook/graph-sdk instead.

comporser.jsonのrequireにfacebook/graph-sdkを追加

composer.phar update
***/MAMP/htdocs/hogeApp/vendor/facebook

にインストールされた。

MySQLのエラー

General error: 1298 Unknown or incorrect time zone: 'Asia/Tokyo'
というエラーが出て接続できなかった。

以下などを参考に次の対応を行った。
3u3.org


MAMP内のMAMP/Library/binにおいて

./mysql_tzinfo_to_sql /usr/share/zoneinfo > hoge.txt

hogeに吐き出されたSQL文をphpMyAdminでデータベースmysqlにて実行する。
結果、エラーが回避された。

BASIC認証

. htaccess  AuthType、AuthName、AuthUserFile、requireを指定
.htpasswd ユーザ名:エンコードされたパスワード

AuthUserFile .htpasswdの場所
AuthName "Please enter your ID and password"
AuthType Basic
require valid-user

通常使うものイディオム

普段使うものを羅列していく。

特定の文字列を検索

find . -name "*.php"|xargs grep hoge
grep hoge_ -rl ./

権限付与

chmod a+x hoge.txt

例でははすべてのユーザに実行権限を与えている。
他には
Linuxコマンド集 - 【 chmod 】 ファイルやディレクトリのアクセス権を変更する:ITpro

一覧

  1. ls -a ドットファイル含む
  2. ls -all すべての情報

ファイル、フォルダ作成

ファイル

touch x.txt

フォルダ

mkdir hoge

名前変更

mv hoge.txt hogeafter.txt

コピー

cp hoge.txt hogecp.txt

シンボリックリンク

ln -s hoge.txt hogesb.txt

ls -allやls -Fでシンボリックリンクなファイルであるかはわかる。

中身をみる

cat hoge.txt