コマンドライン
コンパイルする場合はswiftcを使う。
$ swiftc -help OVERVIEW: Swift compiler USAGE: swiftc [options] <inputs> MODES: -dump-ast Parse and type-check input file(s) and dump AST(s) -dump-parse Parse input file(s) and dump AST(s) -dump-scope-maps <expanded-or-list-of-line:column> Parse and type-check input file(s) and dump the scope map(s) -dump-type-refinement-contexts Type-check input file(s) and dump type refinement contexts(s) -emit-assembly Emit assembly file(s) (-S) -emit-bc Emit LLVM BC file(s) -emit-executable Emit a linked executable -emit-ir Emit LLVM IR file(s) -emit-library Emit a linked library -emit-object Emit object file(s) (-c) -emit-sibgen Emit serialized AST + raw SIL file(s) -emit-sib Emit serialized AST + canonical SIL file(s) -emit-silgen Emit raw SIL file(s) -emit-sil Emit canonical SIL file(s) -parse Parse input file(s) -print-ast Parse and type-check input file(s) and pretty print AST(s) -typecheck Parse and type-check input file(s) OPTIONS: -application-extension Restrict code to those available for App Extensions -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement. -continue-building-after-errors Continue building, even after errors are encountered -driver-time-compilation Prints the total time it took to execute all compilation tasks -D <value> Marks a conditional compilation flag as true -embed-bitcode-marker Embed placeholder LLVM IR data as a marker -embed-bitcode Embed LLVM IR bitcode as data -emit-dependencies Emit basic Make-compatible dependencies files -emit-module-path <path> Emit an importable module to <path> -emit-module Emit an importable module -emit-objc-header-path <path> Emit an Objective-C header file to <path> -emit-objc-header Emit an Objective-C header file -fixit-all Apply all fixits from diagnostics without any filtering -fixit-code Get compiler fixits as code edits -framework <value> Specifies a framework which should be linked against -F <value> Add directory to framework search path -gdwarf-types Emit full DWARF type info. -gline-tables-only Emit minimal debug info for backtraces only -gnone Don't emit debug info -g Emit debug info. This is the preferred setting for debugging with LLDB. -help Display available options -import-underlying-module Implicitly imports the Objective-C half of a module -index-store-path <path> Store indexing data to <path> -I <value> Add directory to the import search path -j <n> Number of commands to execute in parallel -L <value> Add directory to library link search path -l<value> Specifies a library which should be linked against -module-cache-path <value> Specifies the Clang module cache path -module-link-name <value> Library to link against when using this module -module-name <value> Name of the module to build -nostdimport Don't search the standard library import path for modules -num-threads <n> Enable multi-threading and specify number of threads -Onone Compile without any optimization -Ounchecked Compile with optimizations and remove runtime safety checks -output-file-map <path> A file which specifies the location of outputs -O Compile with optimizations -o <file> Write output to <file> -parse-as-library Parse the input file(s) as libraries, not scripts -parse-sil Parse the input file as SIL code, not Swift source -parseable-output Emit textual output in a parseable format -profile-coverage-mapping Generate coverage data for use with profiled execution counts -profile-generate Generate instrumented code to collect execution counts -sanitize-coverage=<type> Specify the type of coverage instrumentation for Sanitizers and additional options separated by commas -sanitize=<check> Turn on runtime checks for erroneous behavior. -save-temps Save intermediate compilation results -sdk <sdk> Compile against <sdk> -serialize-diagnostics Serialize diagnostics in a binary format -static-executable Statically link the executable -static-stdlib Statically link the Swift standard library -suppress-warnings Suppress all warnings -swift-version <vers> Interpret input according to a specific Swift language version number -target-cpu <value> Generate code for a particular CPU variant -target <value> Generate code for the given target -tools-directory <directory> Look for external executables (ld, clang, binutils) in <directory> -use-ld=<value> Specifies the linker to be used -version Print version information and exit -v Show commands to run and use verbose output -warnings-as-errors Treat warnings as errors -whole-module-optimization Optimize input files together instead of individually -Xcc <arg> Pass <arg> to the C/C++/Objective-C compiler -Xlinker <value> Specifies an option which should be passed to the linker
そしてswiftというコマンドもある。
$ swift -h OVERVIEW: Swift compiler USAGE: swift [options] <inputs> OPTIONS: -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement. -continue-building-after-errors Continue building, even after errors are encountered -D <value> Marks a conditional compilation flag as true -framework <value> Specifies a framework which should be linked against -F <value> Add directory to framework search path -gdwarf-types Emit full DWARF type info. -gline-tables-only Emit minimal debug info for backtraces only -gnone Don't emit debug info -g Emit debug info. This is the preferred setting for debugging with LLDB. -help Display available options -index-store-path <path> Store indexing data to <path> -I <value> Add directory to the import search path -j <n> Number of commands to execute in parallel -L <value> Add directory to library link search path -l<value> Specifies a library which should be linked against -module-cache-path <value> Specifies the Clang module cache path -module-link-name <value> Library to link against when using this module -module-name <value> Name of the module to build -nostdimport Don't search the standard library import path for modules -num-threads <n> Enable multi-threading and specify number of threads -Onone Compile without any optimization -Ounchecked Compile with optimizations and remove runtime safety checks -O Compile with optimizations -sdk <sdk> Compile against <sdk> -static-executable Statically link the executable -static-stdlib Statically link the Swift standard library -suppress-warnings Suppress all warnings -swift-version <vers> Interpret input according to a specific Swift language version number -target-cpu <value> Generate code for a particular CPU variant -target <value> Generate code for the given target -use-ld=<value> Specifies the linker to be used -version Print version information and exit -v Show commands to run and use verbose output -warnings-as-errors Treat warnings as errors -Xcc <arg> Pass <arg> to the C/C++/Objective-C compiler -Xlinker <value> Specifies an option which should be passed to the linker
Swiftは対話モードで実行できる。
$ swift Welcome to Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42). Type :help for assistance. 1> 3+2 $R0: Int = 5 2> class A { 3. var hoge:Int? = nil 4. func hello() { 5. print("hello \(hoge)") 6. } 7. } 8> let a = A() a: A = { hoge = nil } 9> a.hoge = 100 10> a.hello() hello Optional(100) 11>
画像
画像処理のコードを書いた。CFDataがNSDataのブリッジなことに気づかなかった..。
文字列ストリーム風
以下でStream(Array(src.characters))としてやれば文字列stream風になる。
class Stream<T> { let m_value:[T] var count = 0 init(_ v:[T]) { m_value = v } func get()->T? { if count < m_value.count { let ret = m_value[count] count = count + 1 print("\(ret)") return ret } else { return nil } } func unget() { count = count - 1 print("unget") if count < 0 { count = count + 1 } } }
GCDの例
SwiftでGCDのgroup等の使い方のサンプルを書いてみた。
// // ViewController.swift // GCDSample // // Created by KatagiriSo on 2017/09/19. // Copyright © 2017年 RodhosSoft. All rights reserved. // import UIKit class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{ struct Menu { let name:String } let menu:[Menu] = [Menu(name: NSStringFromSelector(#selector(AsyncBarrier))), Menu(name: NSStringFromSelector(#selector(GroupWait))), Menu(name: NSStringFromSelector(#selector(GroupNotify))), Menu(name: NSStringFromSelector(#selector(GroupEnterLeave))), Menu(name: NSStringFromSelector(#selector(SemaphoExample))) ] func makeEqualConstraint(item:Any, toItem:Any, attribute:NSLayoutAttribute) -> NSLayoutConstraint{ return NSLayoutConstraint(item: item, attribute: attribute, relatedBy: .equal, toItem: toItem, attribute: attribute, multiplier: 1.0, constant: 0) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let tableView:UITableView = UITableView(frame: self.view.bounds) tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") self.view.addSubview(tableView) let l = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .left) let r = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .right) let t = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .top) let b = makeEqualConstraint(item: tableView, toItem: self.view, attribute: .bottom) l.isActive = true r.isActive = true t.isActive = true b.isActive = true } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return menu.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = menu[indexPath.row].name return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selector = NSSelectorFromString(menu[indexPath.row].name) self.perform(selector) } func AsyncBarrier() { print("\(#function)") let queue = DispatchQueue(label: "asyncBarrierTest") [1,2,3,4,5,6,7,8,9,10].forEach { n in queue.async { print("done \(n)") } } queue.async(flags:.barrier) { print("all done") } print("\(#function) end") } func GroupWait() { print("\(#function)") let queue = DispatchQueue(label: "groupWaitTest") let group = DispatchGroup() for n in 1...100 { queue.async(group: group) { print("done \(n)") } } print("group.wait") let _ = group.wait(timeout: DispatchTime.distantFuture) print("\(#function) end") } func GroupNotify() { print("\(#function)") let queue = DispatchQueue(label: "GroupNotify") let group = DispatchGroup() for n in 1...100 { queue.async(group: group) { print("done \(n)") } } group.notify(queue: queue) { print("done") } print("\(#function) end") } func GroupEnterLeave() { print("\(#function)") let queue = DispatchQueue(label: "GroupNotify") let group = DispatchGroup() for n in 1...100 { group.enter() queue.async { print("done \(n)") group.leave() } } group.notify(queue: queue) { print("done") } print("\(#function) end") } func SemaphoExample() { print("\(#function)") let semapho = DispatchSemaphore(value: 0) // semapho.wait() DispatchQueue.global().async { for n in 1...100 { print("count \(n)") } semapho.signal() } print("wait") semapho.wait() print("\(#function) end") } }
View
rederによってViewがレンダリングされる。その部分をみてみる。
public function render($view = null, $layout = null) { // すでにレンダリングされているなら終わり if ($this->hasRendered) { return null; } // レイアウトがメソッドの引数に指定されているならそれをレイアウトとして使う $defaultLayout = null; if ($layout !== null) { $defaultLayout = $this->layout; $this->layout = $layout; } $viewFileName = $view !== false ? $this->_getViewFileName($view) : null; if ($viewFileName) { $this->_currentType = static::TYPE_TEMPLATE; /// レンダリングイベント通知 $this->dispatchEvent('View.beforeRender', [$viewFileName]); /// _render関数でレイアウトし、ブロックのcontentとしてsetする。 $this->Blocks->set('content', $this->_render($viewFileName)); /// レンダリング終了通知 $this->dispatchEvent('View.afterRender', [$viewFileName]); } /// レウアウトがあるならrenderLayoutでレンダリングし、ブロックのcontentとしてセットする。 if ($this->layout && $this->autoLayout) { $this->Blocks->set('content', $this->renderLayout('', $this->layout)); } if ($layout !== null) { $this->layout = $defaultLayout; } $this->hasRendered = true; /// ブロックのcontentを取得し返却する。 return $this->Blocks->get('content'); }
_render関数、ここが本体になる。テンプレートの継承などもここで解決する。
protected function _render($viewFile, $data = []) { // 引数で$dataが指定されていなければviewVarsを$dataとして使う。 if (empty($data)) { $data = $this->viewVars; } $this->_current = $viewFile; $initialBlocks = count($this->Blocks->unclosed()); // イベント通知 $this->dispatchEvent('View.beforeRenderFile', [$viewFile]); // テンプレートを_evaluateする。ここでファイル内のコードが実行される? $content = $this->_evaluate($viewFile, $data); // イベント通知 $afterEvent = $this->dispatchEvent('View.afterRenderFile', [$viewFile, $content]); if ($afterEvent->getResult() !== null) { $content = $afterEvent->getResult(); } // ここで継承しているテンプレートがあればアサインし if (isset($this->_parents[$viewFile])) { $this->_stack[] = $this->fetch('content'); $this->assign('content', $content); // 親のレンダリング $content = $this->_render($this->_parents[$viewFile]); $this->assign('content', array_pop($this->_stack)); } $remainingBlocks = count($this->Blocks->unclosed()); if ($initialBlocks !== $remainingBlocks) { throw new LogicException(sprintf( 'The "%s" block was left open. Blocks are not allowed to cross files.', $this->Blocks->active() )); } return $content; }
$this->Blocksのところ、ビューブロックをおさえる必要がある。
ViewBuilder
ビューを作る。
build()のところで作っている。
public function build($vars = [], ServerRequest $request = null, Response $response = null, EventManager $events = null) { $className = $this->_className; if ($className === null) { $className = App::className('App', 'View', 'View') ?: 'Cake\View\View'; } if ($className === 'View') { $className = App::className($className, 'View'); } else { $className = App::className($className, 'View', 'View'); } if (!$className) { throw new MissingViewException(['class' => $this->_className]); } $data = [ 'name' => $this->_name, 'templatePath' => $this->_templatePath, 'template' => $this->_template, 'plugin' => $this->_plugin, 'theme' => $this->_theme, 'layout' => $this->_layout, 'autoLayout' => $this->_autoLayout, 'layoutPath' => $this->_layoutPath, 'helpers' => $this->_helpers, 'viewVars' => $vars, ]; $data += $this->_options; return new $className($request, $response, $events, $data); }
builderのbuild()はViewVarsTraitのcreateView($viewClass = null)で呼ばれている。
ViewVarsTraitはControllerで使われている。
Controllerのrender($view = null, $layout = null)関数。
public function render($view = null, $layout = null) { // ビュービルダーを取得 $builder = $this->viewBuilder(); // ビルダーにテンプレートパスが設定されてないならコントローラの_viewPath()で設定する。 if (!$builder->getTemplatePath()) { $builder->setTemplatePath($this->_viewPath()); } // リクエストにbareパラムがあるならAutoLayoutを切る if ($this->request->getParam('bare')) { $builder->enableAutoLayout(false); } $builder->getClassName($this->viewClass); // コントローラのautoRenderを切る $this->autoRender = false; // beforeRenderイベントを通知 $event = $this->dispatchEvent('Controller.beforeRender'); if ($event->getResult() instanceof Response) { return $event->getResult(); } if ($event->isStopped()) { return $this->response; } // ビルダーにテンプレートが設定されなく、リクエストにアクションが設定されているなら、そのアクションからテンプレートを引っ張ってきて設定する。 if ($builder->getTemplate() === null && $this->request->getParam('action')) { $builder->setTemplate($this->request->getParam('action')); } // ビューを生成する。 $this->View = $this->createView(); // ビューをレイアウトを使ってレンダリングする。 $this->response->body($this->View->render($view, $layout)); return $this->response; }
renderはControllerのactionで明示的に呼ぶこともできるがautoRenderを設定しているときはactionが呼ばれたときに自動的に呼ばれている。その機構はActionDispatcherの_invoke(Controller $controller)にある。
protected function _invoke(Controller $controller) { // invokeControllerイベントを通知する。 $this->dispatchEvent('Dispatcher.invokeController', ['controller' => $controller]); $result = $controller->startupProcess(); if ($result instanceof Response) { return $result; } // コントローラのinvokeActionを呼ぶ ここでコントローラのアクションが呼ばれる。 $response = $controller->invokeAction(); // アクションからのレスポンスがなくてautoRenderがtrueならレンダリングしてそのresponseを返却する。 if ($response !== null && !($response instanceof Response)) { throw new LogicException('Controller actions can only return Cake\Http\Response or null.'); } if (!$response && $controller->autoRender) { $response = $controller->render(); } elseif (!$response) { $response = $controller->response; } $result = $controller->shutdownProcess(); if ($result instanceof Response) { return $result; } return $response; }
この_invoke関数はdispatch(ServerRequest $request, Response $response)関数から呼ばれる。
public function dispatch(ServerRequest $request, Response $response) { if (Router::getRequest(true) !== $request) { Router::pushRequest($request); } // beforeDispatchを通知 $beforeEvent = $this->dispatchEvent('Dispatcher.beforeDispatch', compact('request', 'response')); $request = $beforeEvent->getData('request'); if ($beforeEvent->getResult() instanceof Response) { return $beforeEvent->getResult(); } // コントローラを生成しているらしい。。 // Use the controller built by an beforeDispatch // event handler if there is one. if ($beforeEvent->getData('controller') instanceof Controller) { $controller = $beforeEvent->getData('controller'); } else { $controller = $this->factory->create($request, $response); } // 生成したコントローラに対して_invokeを実行。 $response = $this->_invoke($controller); // requestにreturnが設定されていたらresponseを返却 if (isset($request->params['return'])) { return $response; } // afterDispatchを通知 $afterEvent = $this->dispatchEvent('Dispatcher.afterDispatch', compact('request', 'response')); // return $afterEvent->getData('response'); }
このdispatchはDispatcherのdispatchで呼ばれる。
public function dispatch(ServerRequest $request, Response $response) { $actionDispatcher = new ActionDispatcher(null, $this->eventManager(), $this->_filters); $response = $actionDispatcher->dispatch($request, $response); if (isset($request->params['return'])) { return $response->body(); } return $response->send(); }
DispatcherはDispatcherFactoryのcreate関数で作られる。
public static function create()
{
$dispatcher = new Dispatcher();
foreach (static::$_stack as $middleware) {
$dispatcher->addFilter($middleware);
}
return $dispatcher;
}
アクションがどのように呼ばれるのか
このあたりで、CakePHPでどのようにアクションが呼ばれるのかについて調べていることを自覚した。
webrootのindex.phoを調べてみる。
// Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/config')); // Run the request/response through the application // and emit the response. $server->emit($server->run());
このあたりがポイントか。このApplicationはBaseApplicationを継承している。BaseApplicationではgetDispatcherでActionDispatcherを作り、__invokeでdispatchしている。
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next) { return $this->getDispatcher()->dispatch($request, $response); }
というわけで
BaseApplicationの__invokeでActionDispatcherがdispatchし、その関数内でコントローラに_invokeする。
_invokeはautoRenderがtrueだとコントローラのrender()関数を呼び、render()関数はビュービルダーを使ってビューをビルドする。という流れのようだ。
Server
サーバは以下の方法でRunnerにApplicationとミドルウェアを追加してrunし、Runnerは__invokeして回るのでリクエストが処理されていくようだ。
public function run(ServerRequestInterface $request = null, ResponseInterface $response = null) { $this->app->bootstrap(); $response = $response ?: new Response(); // リクエスト取得 $request = $request ?: ServerRequestFactory::fromGlobals(); // ミドルウェア $middleware = $this->app->middleware(new MiddlewareQueue()); if (!($middleware instanceof MiddlewareQueue)) { throw new RuntimeException('The application `middleware` method did not return a middleware queue.'); } // ミドルウェア生成イベント通知 $this->dispatchEvent('Server.buildMiddleware', ['middleware' => $middleware]); // まずミドルウェアとしてApplicationを登録 $middleware->add($this->app); // runnerにrunしレスポンスを取得 $response = $this->runner->run($middleware, $request, $response); if (!($response instanceof ResponseInterface)) { throw new RuntimeException(sprintf( 'Application did not create a response. Got "%s" instead.', is_object($response) ? get_class($response) : $response )); } // レスポンスを返却 return $response; }
このrunはMiddlewareDispatcherにてexecute($request)されるようだ。
public function execute($request) { try { $reflect = new ReflectionClass($this->_class); $app = $reflect->newInstanceArgs($this->_constructorArgs); } catch (ReflectionException $e) { throw new LogicException(sprintf( 'Cannot load "%s" for use in integration testing.', $this->_class )); } // Spy on the controller using the initialize hook instead // of the dispatcher hooks as those will be going away one day. EventManager::instance()->on( 'Controller.initialize', [$this->_test, 'controllerSpy'] ); $server = new Server($app); $psrRequest = $this->_createRequest($request); return $server->run($psrRequest); }
View
AppViewが基底でViewを継承している。
自作のヘルパーなどはinitialize内で $this->loadHelper('MyUtils');してやることで使えるようになる。
View
EventDispatcherInterfaceを実装している。
使用しているtrait
CellTrait
EventDispatcherTrait;
LogTrait;
RequestActionTrait;
ViewVarsTrait;
変数
_helpers
Blocks
plugin
name ビューを作ったコントローラの名前
helpers ビルトインされているヘルパー
templatePath テンプレートのパス
template テンプレートの名前
layout 使用しているレイアウト
layoutPath 使用しているレイアウトの名前
autoLayout 自動レイアウトのonoff
_ext テンプレートの拡張子 デフォルトはctp
subDir
theme ビューのテーマ
hasRendered ビューがすでにレンダリングされているか
uuids 生成されたDOMのuuid
request
response
elementCache
_passedVars 関連するコントローラからの変数
_paths
_pathsForPlugin
_parents ビューの継承元。View::extend()で使用。
_current
_currentType
_stack ネストされたテンプレート。View::extend()で使用。
TYPE_TEMPLATE
TYPE_ELEMENT
TYPE_LAYOUT
メソッド
initialize()
templatePath($path = null) テンプレートのセットとゲット
layoutPath($path = null) レイアウトのセットとゲット
autoLayout($autoLayout = null)
theme($theme = null)
template($name = null)
layout($name = null)
element($name, array $data = , array $options = ) エレメントの描画。_renderElementを使う。
cache(callable $block, array $options = )
elementExists($name) エレメントがあるかのチェック
render($view = null, $layout = null) 与えられたテンプレートとレイアウトでレンダリングする。
renderLayout($content, $layout = null) レイアウトのレンダリング
getVars() $this->viewVarsの名前を返す
get($var, $default = null) view変数を取得
blocks() ビューブロックのキーを返す。$this->Blocks->keys();
start($name) ビューブロックのスタート $this->Blocks->start($name);
append($name, $value = null) ビューブロックの追加 $this->Blocks->concat($name, $value);
prepend($name, $value) $this->Blocks->concat($name, $value, ViewBlock::PREPEND);
assign($name, $value) ビューブロックのセット $this->Blocks->set($name, $value);
reset($name) ビューブロックのリセット $this->assign($name, '');
fetch($name, $default = '') ビューブロックのコンテンツを取得 $this->Blocks->get($name, $default);
end() ビューブロックのエンド $this->Blocks->end();
exists($name) ビューブロックの存在確認 $this->Blocks->exists($name);
extend($name) テンプレートの継承
uuid($object, $url) uuidの取得
getCurrentType()
__get($name)
__set($name, $value)
loadHelpers() ヘルパーの読み込み
_render($viewFile, $data = ) レンダリング
_evaluate($viewFile, $dataForView)
helpers() ヘルパー
loadHelper($name, array $config = []) ヘルパーの読み込み
_getViewFileName($name = null)
_inflectViewFileName($name)
_checkFilePath($file, $path)
pluginSplit($name, $fallback = true)
_getLayoutFileName($name = null)
_getElementFileName($name, $pluginCheck = true)
_getSubPaths($basePath)
_paths($plugin = null, $cached = true)
_elementCache($name, $data, $options)
_renderElement($file, $data, $options)
flexbox その2
flexboxを改めてやる。以下のサイトの記事を読んだ。
flexアイテムとflexコンテナ
flexコンテナは多数のflexアイテムを内包する。flexコンテナにしたい要素に display: flex;を設定する。
すると子要素は自動的にflexアイテムになる。
並べる方向
flex-direction: row; で横方向に並べる。 row-reverseなら右から左になる。columnで上から下。column-reverseで下から上。
まとめて設定
flex-flow: row nowrap;
右づめで表示したい。
justify-content: flex-end; でdirectionがrowなら右づめになる。flex-startなら左づめ、centerなら中央、space-betweenなら等間隔に間をあけて表示される。space-aroundなら端っこにも空間があいた等間隔。
横にならべておいてアイテムの高さはコンテナいっぱいにひろげたい。
横に並べている(row)のときの縦をクロス軸と呼ぶ。
align-items: stretch; を設定すれば良い。
縦方向は上詰めにしたい場合はalign-items: flex-start;。下詰めにしたいときはflex-end。縦方向に中央に配置したい場合はcenter。baselaineに沿わせるならbaseline。
横にならべておいて、複数行の行間を調整したい。
align-content: stretch; コンテナの高さに基づいて均等配置。flex-startなら上からつまる。flex-endなら下からつまる。centerは中央。上端から下端で等間隔ならspace-between。上端と下端で端っこにも空間があいた等間隔ならspace-around。
アイテムの並び順
アイテムに対して order: 2; 等と設定。
アイテムを指定して並べ方を指定
align-self
URLヘルパ
URLを作る。
$this->Url->build(['action' => 'home', $article->id])
で
/kagero3/articles/view/1
ができる。
エレメントとHTMLヘルパ等と組み合わせて次のようなものが作れた。
<?= $this->Html->tag( "a", $this->Element( "agora_card", [ "title" => $article->title, "body" => $article->body ] ) , ["href" => $this->Url->build(['action' => 'home', $article->id]) ] ) ?>
<a url="/kagero3/articles/view/1"><div style="width:200px; height:200px; background-color:white;"> <div> <p> タイトル </p> </div> <div> <p> 内容 </p> </div> </div></a>
ちょっと面白くなってきた。
HTMLヘルパー その2
以下を参考に色々さわっている。
qiita.com
CakePHP 1.3 - Htmlヘルパー
画像リンク
<?= $this->Html->image( 'cake.power.gif', ["alt" => "cake.power", "url" => [ "controller" => "Articles", "action" => "home", "1"] ] ); ?>
<a href="/kagero3/articles/home/1"><img src="/kagero3/img/cake.power.gif" alt="cake.power"/></a>
urlを指定しないと単純にimageタグができる。
<?= $this->Html->image( 'cake.power.gif', ["alt" => "cake.power"] ); ?>
<img src="/kagero3/img/cake.power.gif" alt="cake.power"/>
タグ
タグ自体を指定して生成することもできる。
<?= $this->Html->tag( 'div', 'hello', ['class' => 'classA'] ); ?>
<div class="classA">hello</div>
よって、タグの入れ子も表現できる。
<?= $this->Html->tag('div',$this->Html->tag( 'p','hello'),['class' => 'classA']); ?>
<div class="classA"><p>hello</p></div>
HTMLヘルパー
単純にリンク
<?= $this->Html->link("hello","http://rodhos.info/") ?>
は次のように変換される。
<a href="http://rodhos.info/">hello</a>
つまり、第1引数が表示する文字で、第2引数がURL。
第2引数は以下のように連想配列で指定することもできる。
<?= $this->Html->link(__('View'), ['action' => 'view', $article->id]) ?>
<a href="/kagero3/articles/view/1">View</a>
もう一つ例として
<?= $this->Html->link("hello",[ "controller" => "Articles", "action" => "home", "1"]) ?>
は次のように変換される。
<a href="/kagero3/articles/home/1">hello</a>
第3引数
第3引数を指定するとaタグに属性をつけられる。
<?= $this->Html->link( "hello", "http://rodhos.info/", ["class" => "button"]) ?>
は次のようになる。
<a href="http://rodhos.info/" class="button">hello</a>
テーブルのところ
CakePHPのbakeで作られたテーブル
<table cellpadding="0" cellspacing="0"> <thead> <tr> <th scope="col"><?= $this->Paginator->sort('id') ?></th> <th scope="col"><?= $this->Paginator->sort('title') ?></th> <th scope="col"><?= $this->Paginator->sort('category_id') ?></th> <th scope="col"><?= $this->Paginator->sort('avatar_id') ?></th> <th scope="col"><?= $this->Paginator->sort('created') ?></th> <th scope="col"><?= $this->Paginator->sort('modified') ?></th> <th scope="col" class="actions"><?= __('Actions') ?></th> </tr> </thead> <tbody> <?php foreach ($articles as $article): ?> <tr> <td><?= $this->Number->format($article->id) ?></td> <td><?= h($article->title) ?></td> <td><?= $article->has('category') ? $this->Html->link($article->category->name, ['controller' => 'Categories', 'action' => 'view', $article->category->id]) : '' ?></td> <td><?= $article->has('avatar') ? $this->Html->link($article->avatar->name, ['controller' => 'Avatars', 'action' => 'view', $article->avatar->id]) : '' ?></td> <td><?= h($article->created) ?></td> <td><?= h($article->modified) ?></td> <td class="actions"> <?= $this->Html->link(__('View'), ['action' => 'view', $article->id]) ?> <?= $this->Html->link(__('Edit'), ['action' => 'edit', $article->id]) ?> <?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $article->id], ['confirm' => __('Are you sure you want to delete # {0}?', $article->id)]) ?> </td> </tr> <?php endforeach; ?> </tbody> </table>