- 投稿日:2020-10-10T21:47:46+09:00
Laravelのファイル、フォルダの役割ついて
【概要】
1.Laravelのファイル
2.Laravelのフォルダ
1.Laravelのファイル
備忘録として残しておきます!
.editorconfig:エディタに関する設定ファイルです
.env:動作環境に関する設定ファイルです
.gitignore:Gitに関するファイルです
.artisan:php artisan serve(localhost:8000)等のコマンドです
.styleci.yml:コードチェッカーのファイルです
.composer.json:composerを利用関係です
.package.json:Javascriptのパッケージ管理ツールであるnpm利用関係です
phpunit.xml:テストプログラム関係です
server.php:サーバー起動時のプログラム関係です
webpack.mix.js:Javascriptのパッケージツールで使用
yarn.lock:yarnのパッケージマネージャが使うアプリが入っています
2.Laravelのフォルダ
app:アプリケーションのプログラムがまとめられています。アプリケーションを作成する際はここにscrpitファイルを作ります。User.php(ユーザー認証設定),Http(アプリケーションにアクセスした際に処理をし、Controllerファイルがあります。)
bootsrap:アプリケーションを起動する際に一番最初に処理が行われます。
config:設定関連ファイルです
database:データベース関連ファイルです
public:JavascriptやCSSなどブラウザで検証ツールで見れる部分になります
resources:リーソスファイルであり、テンプレートファイル等があります
routes:ルーティング設定はここになります。web.phpがrubyでいうroutes.rbファイルにあたります。このフォルダの中身には、api.php.channels.php、console.phpがあります
storage:ログファイルが保存されます
tests:単体テスト
vendor:フレームワーク(ここではLaravel)のプログラムが入っています。
- 投稿日:2020-10-10T20:55:42+09:00
【PHP】Valitronを用いたvalidation実装
php初心者の頃はよくわからなかったけど使っていくうちに使いやすさに惚れたバリデーションライブラリです。
環境
・PHP 7.3.18
・composerインストール済み導入
composerを用いてインストールします。
(公式github:https://github.com/vlucas/valitron#installation)php composer.phar require vlucas/valitron実装
・入力フォーム等でPOST送信がされた際に以下実行
・$_POST送信で送られてきたname属性を指定します。$v = new \Valitron\Validator($_POST); //'required'で入力必須項目を指定、message()にてエラー時の出力文字を指定します $v->rule('required', ['name', 'nameKana', 'phoneNumber', 'emailAddress'])->message('※{field}が未入力です。'); $v->rule('required', 'agreement')->message('※{field}の同意にチェックを入れてください。'); $v->rule('required', 'gender')->message('※{field}を選択してください。'); //上記'required'部分をemailにすることでメールアドレスの指定も可能 $v->rule('email', 'emailAddress')->message('※{field}を正しく入力してください。'); //'regex'で数字を指定 $v->rule("regex", "phoneNumber", "/\A0[0-9]{9,10}\z/")->message('※{field}を正しく入力してください。'); //validationエラー時、上記で指定したname属性部分を予め日本語にしておけば日本語で出力されます。 $v->labels([ 'name' => 'お名前', 'nameKana' => 'フリガナ', 'emailAddress' => 'メールアドレス', 'gender' => '性別', 'phoneNumber' => '電話番号', 'agreement' => '個人情報保護方針' ]);//上記続き if ($v->validate()) { //バリデーションOK時の挙動(送信確定画面に遷移等) }else{ $errors = $v->errors(); //バリデーションNG時の挙動(再入力画面に遷移等) }validationエラー時、$errorsは配列で返ってくるのでhtml側でforeachで回して表示します。
- 投稿日:2020-10-10T19:03:34+09:00
Vue + Wordpress + Heroku + S3でポートフォリオを構築する
1. はじめに
こんにちは。ツダと申します。私はカメラが趣味で、自分の写真のポートフォリオサイトを作成したいと思い、Vue.jsとWordpressを使って作成しました。
この記事では、私がポートフォリオを作るうえで行ったことについて紹介させていただければと思っています。
2. 技術スタック
- Vue.js : @vue/cli 4.5.6
- Wordpress : 5.5.1
- PHP : 7.3.5
- heroku : heroku/7.44.0 win32-x64 node-v12.16.2
- S3
簡単なシーケンス図(トップ画面表示時)
3. Wordpress環境を整える
3.1 概要
今回のポートフォリオでは、WordpressをAPIとして使用します。 ですので、フロントエンド側はVueを、バックエンド側はWordpressというような役割分担をするイメージです。
参考:WP REST API
3.2 Localbyflywheelをダウンロード
まずはWordpress環境を構築します。環境構築にはLocalbyFlywheelを用います
。環境構築方法については以下のサイトを参考にしていただければと思います。3.3 DB設定
後述しますが、デプロイするサーバは「heroku」を使います。herokuで使用するDBとローカル環境で使用するDBが異なりますので、wp-config.phpに条件分岐を書きます。wp-config.phpは作成したWordpressプロジェクトのルートフォルダに配置してあります(Windowsの場合、C:\Users\<ユーザ名>\Local Sites\<作成したアプリ名>\app\public配下)
wp-config.php...(省略)... // ** MySQL settings - You can get this info from your web host ** // if ( @$_SERVER["SERVER_NAME"] === '<作成したWordpressのローカル環境のドメイン名>' ) { // ローカル環境の設定 define( 'DB_NAME', 'local' ); define( 'DB_USER', 'root' ); define( 'DB_PASSWORD', 'root' ); define( 'DB_HOST', 'localhost' ); } else { // heroku環境の設定 $url = parse_url(getenv('CLEARDB_DATABASE_URL')); define('DB_NAME', trim($url['path'], '/')); define('DB_USER', $url['user']); define('DB_PASSWORD', $url['pass']); define('DB_HOST', $url['host']); } define( 'DB_CHARSET', 'utf8' ); define( 'DB_COLLATE', '' ); ...(省略)...参考:PaaS入門 〜Heroku + wordpress〜
3.4 セキュリティ面の設定
WordPressはあくまでAPIとしての役割で使用するため、WordPressサイトにはアクセスされたくありません。そこで、直接アクセスされるのを防ぐための各種設定を行います。
ログイン画面のパスを変更する
WordPressはログイン画面へのパスがデフォルトで "/wp-admin" として設定されています。しかし、これではログイン画面へのアクセスが容易にできてしまうので、これを変更するためのプラグイン「Login Rebuilder」が用意されています。
参考: Login Rebuilderの使い方|WordPressのログインURLの変更方法
サイトへのアクセスを制御する
次にサイトへのアクセスを制御します。何も設定していない場合、サイトへアクセスした際に記事が表示されてしまいますので、表示されないように設定します。
設定するためには、設定しているテーマのfunctions.phpに下記のコードを追加します。
functions.phpfunction access_restriction() { // 許可するアクセス // 管理画面 if (is_admin()) { return; } // APIのアクセス if (strpos($_SERVER['REQUEST_URI'],'wp-json')) { return; } // ログイン画面 if (strpos($_SERVER['REQUEST_URI'],'<Login Rebuilderで設定したログイン画面へのパス>')) { return; } wp_die('アクセスできません', 'アクセス拒否', array('responce' => 403)); } add_action('init', 'access_restriction');管理画面、APIへのアクセス、およびログイン画面へのアクセスは必要なので許可し、それ以外を拒否するように設定しています。実際にアクセスしてみると下のような拒否画面が現れます。
参考:WordPress へのアクセス制限を functions.php で設定する
3.5 S3を使うための設定をする
Herokuは定期的にビルドを行っているため、アップロードした画像が消えてしまう現象が発生します。
herokuのリポジトリがdynoという単位で管理されており、Freeプランだと一定時間経過後にdynoが再起動される仕様になっているから。 - 【Rails6】herokuで投稿した画像が表示されない
そこでAWSの「S3」というサービスを用いることで画像が消える現象を回避していきます。Wordpressのプラグインを用いてアップロードする画像の格納先をS3に設定することで、外部に画像を保存します。
S3を使用するための準備をする
S3のアクセス権限の設定
ブロックパブリックアクセスは下記のように設定。
この設定によって以下の二つを達成することを確認しております。
- WordPressからの画像アップロード
- ポートフォリオサイトからの画像の表示
バケットポリシーの設定は次のようにしています。
バケットポリシー{ "Version": "2008-10-17", "Statement": [ { "Sid": "AllowPublicRead", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "s3:GetObject", "Resource": "<リソース名>/*" } ] }参考:
- WordPress S3 Tutorial: How to Connect WordPress to Amazon S3 Bucket
- Amazon S3 コンソールを使用して、バケットの ACL でパブリックアクセスを有効にしました。誰でもバケットにアクセスが可能ですか ?
ローカル環境でS3へつなげるようにする
私がローカル環境でテストを行った際、WordpressからS3につなげず、以下のようなエラーが発生しました。
Error Getting Bucket Region —There was an error attempting to get the region of the bucket tsudawork: Error executing "GetBucketLocation" on "<バケットのURL>?location"; AWS HTTP error: cURL error 60: (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)
内容はCURLでS3へつなぐ際にコード60のエラーが発生しているというものです。コード60は次のようになっています。
CURLE_PEER_FAILED_VERIFICATION (60)
The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK. This error code has been unified with CURLE_SSL_CACERT since 7.62.0. Its previous value was 51. - libcurl error codesこれだけでは少しわかりづらいですが、調べていると次のような記述を発見しました。
OSにバンドルされているルート証明書などが古いので証明書の検証ができない様子。 - curl: (60) SSL certificate problem, verify that the CA cert is OK
私の解釈ですが、つなぎ先サーバのSSL証明書とつなぎ元端末のSSL証明書を検証した際、つなぎ元端末のSSL証明書が存在しないor古いために発生するエラーと考えられます。こちらは次の対策をすることで解決しました。
まず、次のサイトより最新の証明書(cacert.pem)をダウンロードします。
CA certificates extracted from Mozilla
次に作成したwordpressプロジェクトのconf/phpフォルダ配下にphp.ini.hbsというファイルがありますので、そのファイルの一番下に次のコードを追加します。
php.ini.hbs[curl] curl.cainfo = "<ダウンロードしたcacert.pemのパス>"Wordpressプロジェクトを再起動し、WP Offload Media Liteの設定画面でS3のバケットを選択すると接続ができるようになっているはずです。
参考:
curl: (60) SSL certificate problem: unable to get local issuer certificate
cURL error 60: SSL certificate problem: certificate has expiredGDをComposerに追加する
WordpressでS3を使用するためにはGD(画像ファイルの操作ライブラリ)の使用が必要です。HerokuにはデフォルトでGDがインストールされてないため、Composerで使えるように設定する必要があります。
まず、composer.jsonに下記のように追記します。
composer.json{ "require": { "ext-gd": "*" } }作ったファイルはwordpressのルートディレクトリに配置します。続いて下記のコマンドを実行します。
$ composer install終了後、composer.lockがcomposer.jsonと同じディレクトリに作成されていればOKです。後はHerokuへのデプロイ時にHerokuが自動的にライブラリをインストールします。
3.6 Herokuへデプロイする
まずは下記の記事を参考に、Herokuが使える状態にします。
参考:Heroku初心者がHello, Herokuをしてみる
そして下記の記事を参考に、作成したWordpressプロジェクトをHerokuへデプロイします。
参考:PaaS入門 〜Heroku + wordpress〜
主に次のことをやる必要があります。
- Wordpressのルートディレクトリで「git init」+「git commit -m "commit"」
- Wordpress用のプロジェクトアプリ作成
- composer.jsonおよびcomposer.lockの作成
- DB作成
- デプロイ
3.7 パーマリンクの設定
WP REST APIにリクエストを投げると、デフォルトの状態では404エラーが返ってくるそうです。そのためにパーマリンクの設定をしてあげます。
※私の環境でこの設定をしなかったところ、 管理画面からの記事の編集・投稿ができませんでしたので設定することがおすすめです。
設定方法
設定方法はいたって簡単です。Wordpressの管理画面左にあるタブから「設定→パーマリンク設定」と進み、共通設定の「投稿名」にチェックをいれるだけとなっております。
これで404エラーの原因となるパーマリンク設定が解消されるはずです。
参考:WP REST APIで404が返ってくる。これはパーマリンク設定のせいだ!
4. Vue.jsでプロジェクトを作成していく
4.1 環境構築
まずVueの環境構築を行います。Vueを使用する方法は主に二つあります
- CDN
- Vue CLI
ここではVue CLIを用いた方法を選択します。「参考」に記載させていただいた記事をもとに、「Welcome to Your Vue.js App」が表示されればOKです。
参考:Vue.js を vue-cli を使ってシンプルにはじめてみる
4.2 必要なモジュールをインストール
- axios
$ npm install axios
- bootstrap-vue
$ npm install vue bootstrap-vue bootstrap
- vue-router
$ npm install vue-router参考:
axios - npm
Getting Started | BootstrapVue
Installation | Vue Router4.3 アプリの全体像
アプリの全体像は次のようになっています。
main.jsimport Vue from 'vue' import Articles from './Articles.vue' import Article from './Article.vue' import Router from 'vue-router' import Top from './Top.vue' import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' // Router Vue.use(Router) // Install BootstrapVue Vue.use(BootstrapVue) // Optionally install the BootstrapVue icon components plugin Vue.use(IconsPlugin) const router = new Router({ mode:'history', routes: [ { path: '/', component: Articles }, { path: '/category/:value', name: 'category', component: Articles, }, { path: '/post/:value', name: 'post', component: Article } ] }) Vue.config.productionTip = false new Vue({ render: h => h(Top), router }).$mount('#app')Top.vue<template> <div id="app"> <b-navbar class="navbar bg-white" fixed="top" toggleable="lg"> <b-navbar-brand href="/">Tsuda Work</b-navbar-brand> <b-navbar-toggle target="nav-collapse"></b-navbar-toggle> <b-collapse id="nav-collapse" is-nav> <b-navbar-nav> <b-nav-item-dropdown :text="category.name" class="nav-item" v-for="category in categories" :key="category.id"> <b-dropdown-item><router-link class="nav-link" :to="{name:'category', params:{value:category.id}}">All</router-link></b-dropdown-item> <b-dropdown-item :text="subCategory.name" class="nav-item" v-for="subCategory in category.subCategories" :key="subCategory.id"> <router-link class="nav-link" :to="{name:'category', params:{value:subCategory.id}}">{{subCategory.name}}</router-link> </b-dropdown-item> </b-nav-item-dropdown> </b-navbar-nav> <b-navbar-nav class="ml-auto"> <b-nav-item class="nav-item" href="https://twitter.com/tsuda215"> <div>Twitter</div> </b-nav-item> </b-navbar-nav> </b-collapse> </b-navbar> <router-view class="article-router" :key="$route.path"></router-view> <footer class="page-footer font-small blue pt-4"> <div class="footer-copyright text-center py-3">© 2019-2020 Tsuda Work.</div> </footer> </div> </template> <script> import axios from 'axios'; import states from "./assets/property.json"; export default { name: 'Top', data() { return { categories: [], } }, mounted() { const url = states.hostname + states.categoriesUrl; (async () => { try { // カテゴリー取得 const res = await axios.get(url); var categoriesTmp = [].concat(res.data); for (var categoryTmp of categoriesTmp) { // サブカテゴリ―の場合は処理をスキップ if (categoryTmp.parent > 0) { continue; } // カテゴリー作成 var category = {}; category['id'] = categoryTmp.id; category['name'] = categoryTmp.name; category['subCategories'] = []; // サブカテゴリー取得 const resForSubCategories = await axios.get(url + "?parent=" + categoryTmp.id); var subCategoriesTmp = [].concat(resForSubCategories.data); for (var subCategoryTmp of subCategoriesTmp) { // サブカテゴリー作成 var subCategory = {}; subCategory['id'] = subCategoryTmp.id; subCategory['name'] = subCategoryTmp.name; // サブカテゴリ―配列に追加 category['subCategories'].push(subCategory); } // カテゴリー配列に追加 this.categories.push(category); } } catch (error) { console.log(error); } })(); }, } </script> <style> #app { display: flex; flex-direction: column; min-height: 82vh; font-family: "Yu Gothic","Yu Gothic UI","Meiryo UI", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "MS PGothic", sans-serif; } .navbar { background-color:rgba(0, 0, 0, 0); } .page-footer { margin-bottom:0; } @media screen and (orientation:portrait) { .article-router { margin-top:10vh; } } @media screen and (orientation:landscape) { .article-router { margin-top:7vw; } @media screen and (max-width: 800px){ .article-router{ margin-top:9vw; } } } </style>Articles.vue<template> <div id="app"> <transition name="fade"> <!-- フェードイン実装のためv-ifは必要 --> <div v-if="ok" class="album py-5"> <div class="container"> <div class="row"> <div class="col-md-4 mb-4" v-for="post in posts" :key="post.title.rendered"> <div class="card h-100 shadow-sm"> <router-link class="nav-link" :to="{name:'post', params:{value:post.id}}"> <img class="card-img-top" :src="post._embedded['wp:featuredmedia'][0].source_url" alt=""> </router-link> </div> </div> </div> </div> </div> </transition> </div> </template> <script> import axios from 'axios'; import states from "./assets/property.json"; export default { name: 'Articles', data() { return { posts: [], // フェードイン実装のために必要 ok: false } }, mounted() { var categoryId = this.$route.params.value; var url = states.hostname + states.postsUrl; if (categoryId != undefined) { url = states.hostname + states.categoryUrl + categoryId + '&_embed'; } (async () => { try { const res = await axios.get(url); this.posts = this.posts.concat(res.data); // マウント時にok=trueを実施 this.ok = true; } catch (error) { console.log(error); } })(); }, } </script> <style> .card { display: flex; justify-content: center; /*左右中央揃え*/ align-items: center; /*上下中央揃え*/ } .fade-enter-active, .fade-leave-active { transition: opacity 1s; } .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { opacity: 0; } </style>Article.vue<template> <transition name="fade"> <div v-if="ok" class="container"> <div class="row"> <div class="articleContainer"> <div class="article"> <h3 class="title">{{post.title.rendered}}</h3> <div class="content" v-html="post.content.rendered"> </div> </div> </div> </div> </div> </transition> </template> <script> import axios from 'axios'; import states from "./assets/property.json"; export default { name: 'Article', data() { return { post: null, // フェードイン実装のために必要 ok: false } }, mounted() { var postId = this.$route.params.value; const url = states.hostname + states.postUrl + postId + '?_embed'; (async () => { try { const res = await axios.get(url); this.post = res.data; // マウント時にok=trueを実施 this.ok = true; } catch (error) { console.log(error); } })(); }, } </script> <style> .articleContainer { display: flex; justify-content: center; /*左右中央揃え*/ align-items: center; /*上下中央揃え*/ width: 100%; } .article > .title { text-align: center; } .article img { width: 100%; } @media screen and (min-width: 800px){ .article{ width:60%; } } @media screen and (max-width: 799px){ .article{ width:85%; } } /* フェードインの設定 */ .fade-enter-active /* , .fade-leave-active */ { transition: opacity 2s; } .fade-enter /* , .fade-leave-to */ { opacity: 0; } .fade-leave-active { display:none; } </style>property.json{ "hostname":"https://<Wordpressプロジェクトのホスト名>/", "postUrl":"wp-json/wp/v2/posts/", "postsUrl":"wp-json/wp/v2/posts?_embed", "categoryUrl":"wp-json/wp/v2/posts?categories=", "categoriesUrl":"wp-json/wp/v2/categories" }※注意
- ホスト名を間違えると"No 'Access-Control-Allow-Origin' header is present on the requested resource."という旨のエラーが出ることを確認しました。
- httpsをhttpにして設定すると、Heroku上のVueから画像が取得できないことを確認しました。
4.4 アプリの解説
Navbar
Navbarを使用する前に、vueでbootstrapを使用できるようにする必要があります。
参考:Getting Started | BootstrapVue
インストールコマンドは次の通りです。
# With npm npm install vue bootstrap-vue bootstrap # With yarn yarn add vue bootstrap-vue bootstrapインストールした後はmain.jsにてbootstrapを使えるように設定しています。
main.jsimport { BootstrapVue, IconsPlugin } from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' ...(省略)... // Install BootstrapVue Vue.use(BootstrapVue) // Optionally install the BootstrapVue icon components plugin Vue.use(IconsPlugin)次にメニュー部分の実装に入ります。
参考:NavbarTop.vue<template> <div id="app"> <b-navbar class="navbar bg-white" fixed="top" toggleable="lg"> <b-navbar-brand href="/">Tsuda Work</b-navbar-brand> <b-navbar-toggle target="nav-collapse"></b-navbar-toggle> <b-collapse id="nav-collapse" is-nav> <b-navbar-nav> <b-nav-item-dropdown :text="category.name" class="nav-item" v-for="category in categories" :key="category.id"> <b-dropdown-item><router-link class="nav-link" :to="{name:'category', params:{value:category.id}}">All</router-link></b-dropdown-item> <b-dropdown-item :text="subCategory.name" class="nav-item" v-for="subCategory in category.subCategories" :key="subCategory.id"> <router-link class="nav-link" :to="{name:'category', params:{value:subCategory.id}}">{{subCategory.name}}</router-link> </b-dropdown-item> </b-nav-item-dropdown> </b-navbar-nav> <b-navbar-nav class="ml-auto"> <b-nav-item class="nav-item" href="https://twitter.com/tsuda215"> <div>Twitter</div> </b-nav-item> </b-navbar-nav> </b-collapse> </b-navbar> <router-view class="article-router" :key="$route.path"></router-view> <footer class="page-footer font-small blue pt-4"> <div class="footer-copyright text-center py-3">© 2019-2020 Tsuda Work.</div> </footer> </div> </template>ポイントは次の部分です。
Top.vue<b-nav-item-dropdown :text="category.name" class="nav-item" v-for="category in categories" :key="category.id"> <b-dropdown-item><router-link class="nav-link" :to="{name:'category', params:{value:category.id}}">All</router-link></b-dropdown-item> <b-dropdown-item :text="subCategory.name" class="nav-item" v-for="subCategory in category.subCategories" :key="subCategory.id"> <router-link class="nav-link" :to="{name:'category', params:{value:subCategory.id}}">{{subCategory.name}}</router-link> </b-dropdown-item> </b-nav-item-dropdown>親カテゴリはAllとして必ず作成するようにしています。そしてサブカテゴリの数だけループを回し、追加のドロップダウンアイテムを作成しています。カテゴリ作成ロジックは次のようにしています。
Top.vuemounted() { const url = states.hostname + states.categoriesUrl; (async () => { try { // カテゴリー取得 const res = await axios.get(url); var categoriesTmp = [].concat(res.data); for (var categoryTmp of categoriesTmp) { // サブカテゴリ―の場合は処理をスキップ if (categoryTmp.parent > 0) { continue; } // カテゴリー作成 var category = {}; category['id'] = categoryTmp.id; category['name'] = categoryTmp.name; category['subCategories'] = []; // サブカテゴリー取得 const resForSubCategories = await axios.get(url + "?parent=" + categoryTmp.id); var subCategoriesTmp = [].concat(resForSubCategories.data); for (var subCategoryTmp of subCategoriesTmp) { // サブカテゴリー作成 var subCategory = {}; subCategory['id'] = subCategoryTmp.id; subCategory['name'] = subCategoryTmp.name; // サブカテゴリ―配列に追加 category['subCategories'].push(subCategory); } // カテゴリー配列に追加 this.categories.push(category); } } catch (error) { console.log(error); } })(); },記事表示部分
記事の表示は次の部分です。
Articles.vue<div class="container"> <div class="row"> <div class="col-md-4 mb-4" v-for="post in posts" :key="post.title.rendered"> <div class="card h-100 shadow-sm"> <router-link class="nav-link" :to="{name:'post', params:{value:post.id}}"> <img class="card-img-top" :src="post._embedded['wp:featuredmedia'][0].source_url" alt=""> </router-link> </div> </div> </div> </div>こちらもbootstrapの使用が必要になります。
フェードインの実装
記事の一覧を表示する際にフェードインを使用しています。
参考:Enter/Leave & List Transitionsフェードインの実装にはvueのtransitionという機能を使用しています。transitionを使用するためにはフェードさせたい要素をで囲む必要があります。
Articles.vue<transition name="fade"> <!-- フェードイン実装のためv-ifは必要 --> <div v-if="ok" class="album py-3"> ...(省略)... </div> </transition>v-ifに指定の"ok"はデフォルトでfalseとなっています。
Articles.vuedata() { return { posts: [], // フェードイン実装のために必要 ok: false } },そしてマウント時にtrueとすることで、要素を表示させます。
Articles.vuemounted() { ...(省略)... (async () => { try { const res = await axios.get(url); this.posts = this.posts.concat(res.data); // マウント時にok=trueを実施 this.ok = true; } catch (error) { console.log(error); } })(); },表示の際は下記のcssを使用しているので、フェードインでの表示となります。
Articles.vue<style> ...(省略)... .fade-enter-active, .fade-leave-active { transition: opacity 1s; } .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { opacity: 0; } </style>Wordpress REST APIを用いて記事取得
記事を取得する際はWordpress REST APIにリクエストを投げますが、リクエストを投げる際はaxiosを使用しています。
Articles.vue<script> import axios from 'axios'; import states from "./assets/property.json"; export default { name: 'Articles', data() { return { posts: [], // フェードイン実装のために必要 ok: false } }, mounted() { var categoryId = this.$route.params.value; var url = states.hostname + states.postsUrl; if (categoryId != undefined) { url = states.hostname + states.categoryUrl + categoryId + '&_embed'; } (async () => { try { const res = await axios.get(url); this.posts = this.posts.concat(res.data); // マウント時にok=trueを実施 this.ok = true; } catch (error) { console.log(error); } })(); }, } </script>実際にリクエストを投げているのは次の部分です。
const res = await axios.get(url);記事のIDを取得したときなど、単数を指定するものはObject、IDを指定せず記事を複数取得した場合はObjectの配列がres.dataには格納されています。後は取得したものを加工し、vueで描画すればOKです。
参考:《WordPress》2017年末にWP REST API で取得してVue.jsで描画するまでのまとめ。
4.5 アプリをHerokuにデプロイする。
「参考」に記載の記事をもとに、大まかには下記の作業を行う必要があります。
- expressのインストール
- server.jsの作成
- デプロイ
これらの作業を完了しアプリを表示すると、Wordpressで作成したアプリに対してリクエストが飛んでいるはずです。無事記事の一覧が表示されていればOKです。
4.6 リロードの設定
Vueアプリはデフォルトでrouterのパスに「ハッシュ(#)」が含まれています。これはrouterを「historyモード」に変更することで削除することができますが、historyモードを使用することでページをリロードした際に、404エラーが返却されてしまいます(historyモードでない場合にはこの問題は発生しません)。
しかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。- HTML5 History モード
しかし、適切な設定をすれば「ハッシュを削除しつつ、リロードにも対応する」ことが可能となります。
connect-history-api-fallbackをインストールする
npm install --save connect-history-api-fallback参考:connect-history-api-fallback
server.jsにコードを追加する
server.jsvar express = require('express'); var path = require('path'); + const history = require('connect-history-api-fallback'); var serveStatic = require('serve-static'); app = express(); + app.use(history()); app.use(serveStatic(__dirname + "/dist")); var port = process.env.PORT || 5000; app.listen(port); console.log('server started '+ port);こちらローカル環境では動作しておりませんが、Herokuでは動作することを確認しております。
参考:vue-routerのルーティングURLからハッシュを除去しつつ、URL直接指定でも表示させる(Node, Express)
5. その他
5.1 スリープ防止のため、スケジューラ作成
Herokuのサイトは一定時間が経過すると、スリープ状態に入ってしまうため、次にアクセスする際にサイトが表示されるまで時間がかかるという問題が発生してしまいます。ここではそれを防止する方法について紹介します。
herokuにスケジューラを追加
まずはアプリケーションフォルダ配下で下記コマンドを実行し、スケジューラのアドオンを追加していきます。
$ heroku addons:create scheduler:standard下記のように表示されれば成功です。
Creating scheduler:standard on ⬢ <アプリ名>... free To manage scheduled jobs run: heroku addons:open scheduler Created <スケジューラの名前> Use heroku addons:docs scheduler to view documentationジョブを作成する
続いて下記コマンドを実行して、ジョブの作成画面を表示します。
$ heroku addons:open schedulerすると下記のような画面が表示されるはずです。
「Create Job」を押下します。
すると下記のようなウィンドウが表示されますので、
Schedule : Every 10 minutes
Run Command : curl <サイトのURL>を入力し、「Save Job」を押下します。
これでジョブの作成は完了です。後は時間をおいてもう一度サイトにアクセスしてみると、表示が遅い問題が解消されているはずです。5.2 Wordpressでアップロードできる画像の大きさを上げる
HerokuにWordpressをデプロイした直後は2MBまでの画像しかアップロードすることができません。この最大値を上げるための設定を行います。
.user.iniの追加
Herokuにはphp.iniの設定を書き換える方法として.user.iniの作成を挙げています。
参考:Customizing Web Server and Runtime Settings for PHP具体的には、まず下記のようなファイルを作成します。
.user.inipost_max_size = 100M upload_max_filesize = 100MそしてこのファイルをWordpressのルートディレクトリに配置します。後はアプリをデプロイするとHerokuが自動で設定値を認識し、画像のアップロードサイズが増加しているはずです。
6. まとめ
以上がVue + Wordpressを用いてポートフォリオを作成した手順となります。初めてVueを用いてWebサイトを作成しましたが、APIの併用もしたことで大変勉強になりました。簡単にはなってしまいますが、以上で記事を締めさせていただこうと思います。最後に、参考にさせていただいた記事の執筆者の方々、本当にありがとうございます。そして勝手に引用させていただき恐縮です。
備考
今回紹介させていただいたポートフォリオは無料で作成することが可能となっております。しかしSSLを常用化したり、独自ドメインを設定したい場合は有料となってしまいます。
参考させていただいたリンクまとめ
Wordpress
WP REST API
WP REST APILocalByFlyweelでの環境構築
超簡単にローカル環境が構築できるLocalbyFlywheelの使い方
Local by Flywheelのダウンロードからインストールまでの手順Wordpress + HerokuのDB設定
PaaS入門 〜Heroku + wordpress〜Login Rebuilder(ログインパスの変更)
Login Rebuilderの使い方|WordPressのログインURLの変更方法phpのアクセス制限
WordPress へのアクセス制限を functions.php で設定するWP REST APIのためのパーマリンク設定
WP REST APIで404が返ってくる。これはパーマリンク設定のせいだ!S3
WordPressの画像データをS3に保存する
Amazon S3 パブリックアクセスブロックの使用
WordPress S3 Tutorial: How to Connect WordPress to Amazon S3 Bucket
Amazon S3 コンソールを使用して、バケットの ACL でパブリックアクセスを有効にしました。誰でもバケットにアクセスが可能ですか ?
CA certificates extracted from Mozilla
curl: (60) SSL certificate problem: unable to get local issuer certificate
cURL error 60: SSL certificate problem: certificate has expiredGD(PHPの画像操作ライブラリ)
Image Processing and GDcomposer
Heroku PHP SupportHerokuへのデプロイ
Heroku初心者がHello, Herokuをしてみる
PaaS入門 〜Heroku + wordpress〜Vue.js
Vue.js環境構築
Vue.js を vue-cli を使ってシンプルにはじめてみる
axios - npm
Getting Started | BootstrapVue
Installation | Vue RouterBootstrapVue
Getting Started | BootstrapVueVueでNavbar
Navbarフェードインの実装
Enter/Leave & List TransitionsWordpressを用いた記事取得
《WordPress》2017年末にWP REST API で取得してVue.jsで描画するまでのまとめ。Herokuへのデプロイ
Vue.jsで作ったアプリをHerokuにデプロイrouterの設定
HTML5 History モード
connect-history-api-fallback
vue-routerのルーティングURLからハッシュを除去しつつ、URL直接指定でも表示させる(Node, Express)その他
Herokuのスリープ防止
herokuを24時間稼働させる設定Herokuでphp.iniを書き換える
Customizing Web Server and Runtime Settings for PHP
- 投稿日:2020-10-10T18:57:24+09:00
アルゴリズム入門[最大公約数]
まずアルゴリズムの学習を始めるにあたって最初に考え方をインプットしておきましょう
まずコンピューターの処理の種類は四つしかありません
入力
、記憶
、演算
、出力
のみですアルゴリズムと聞くと難しいと思うかもしれませんが、この4つを組み合わせて最高の処理の形を探すだけです
同様にして処理の流れも三つしかありません
順次
、分岐
、繰り返し
のみです次に問題を解くコツ4つを見ていきましょう
- 処理の区切りを考える
- キリの悪い数字を疑う
- 数値の変化を追う
- 計算量は無視してとりあえず動くようにしてみる
- より良い処理を考える
こんな感じですね
ここを抑えてユークリッドの互除法を考えていきましょう
そもそもユークリッドの互除法とは二つの最大公約数を求めるアルゴリズム
です二つの整数の大きい方から小さい方を引くことを二つの整数が等しくなるまで繰り返す 等しくなった値が最大公約数ではまずなぜそうなるのかを考えましょう
100
と25
の最大公約数を求めていきましょう
100=5^2*2^2
25=5^2
ですね
つまり共通している5^2
が最大公約数
これを上のアルゴリズムで計算すると
100>25なので
なので最大公約数は25
100-25=75
75>25なので
75-25=50
50>25なので
50-25=25
25=25ではなぜこうなるのかを解説していきます
縦25、横100の長方形から縦25、横25の正方形を取り除くことができるのでユークリッドの互除法です
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * しかしこれだと数が大きくなれば大きくなるほど、計算回数が増えてしまうため、
剰余を求めてやるのが先ほど紹介したコツにある、より良い処理を考えるというところになりますfunction euclidean(int $x, int $y) { if ($x < $y) return euclidean($y, $x); if ($y == 0) return $x; return euclidean(abs($y), intval($x % $y)); }まず
$x >= $y
の状態を作りたいので$x
の方が$y
よりも小さい場合は数値を入れ替えて計算しなおします
また$y
が0
の場合も必ず$x
になる上に割る数のエラーが出てしまうので$x
を返しています
- 投稿日:2020-10-10T17:30:05+09:00
LaravelのSeederで混乱した話
はじめに
自分のスキル感
実務でLaravel,PHPを触り始めて4ヶ月程度
バリバリの駆け出しエンジニアです。実務でseederを扱って混乱したことがあるので、メモ書き程度にまとめておきます。
同じような境遇に人の役に立てば幸いです。独学で勉強している時はあまりなんとも思わなかったのですが、環境の差異で軽く混乱しました。
勉強しているは平気で本番環境のデータベースををmigrateしなおしたり、
データを流し直したりしまくっていましたが、実務ではそうはいきません。(注)自分が経験した現場での話なので、やり方は多数あるかもしれませんので、その辺はご了承ください。
Laravelのseeder作成から反映させるまでの流れ
自分が実務で経験した本番環境のデータベースをデータを変更したりする場合の手順は以下の通り
- データを変更するseeder(ここでは適当に
ExampleSeeder.php
とする)を作成+変更部分を記述- laravelの場合,
DatabaseSeeder.php
へ作成したSeederファイルの追加(DatabaseSeeder.php
の中に含まれる元からあるSeederファイルを変更するという手もありますが、あまり良くないかも)- コードが良さそうであれば、マージした後に、以下のコマンドで変更を加えたSeederを本番環境で流す
$ php artisan db:seed --class=ExampleSeeder雑にまとめるとこんな感じだろうか。
どこで混乱したか
上記の流れで厄介なのが、2の手順である。
もし仮に2の手順を飛ばしてマージされたとしたら、、、どうなるのか。。(ガクブル)
本番環境では単独でseederファイルを流します。
ローカルはどうなるかというと、、、
変更部分がマージされた後に、masterブランチpullしてきて、下記のコマンドを叩きます。$ php artisan migrate:fresh --seedすると、、、
本番環境では反映されているデータの変更が、ローカルには反映されません。(ここではExampleSeederが流れない)
なぜか。
php artisan migrate:fresh --seed
上記コマンドで流れるseederとは
DatabaseSeeder.php
に記載しているseederファイルだからです。
このコマンドを叩いたから全てのseederファイルが流れるわけではありませんここが注意すべきところです。
1と3だけでも本番環境には反映させることができます。
しかし、ローカル環境でも変更を反映させるためには変更を加えたseederファイルを
DatabaseSeeder.php
へ追加する必要があるのです。こういうのが積み重なり、本番環境とローカル環境,develop環境など、環境ごとに差異が出てきます。
そして本番環境でしか起こり得ないバグなど、ローカルでは調査しづらいバグの根本的な原因にもなってくるわけです。
恐ろしいSeederを使う人は注意しましょう。。。。
終わりに
最後まで読んでいただいてありがとうございます。
何か間違っていることがあればぜひぜひご指摘ください!
- 投稿日:2020-10-10T17:07:19+09:00
php.ini (memory) に 1時間持っていかれた話
目標ができ、アウトプットするために記事を書こうと思いました。
がんばるぞい!
環境
・PHP 7.3
・Composer 1.10.13
問題
個人開発をネタにしようと何を作ろうか悩みながら
composerでインストールしていたら
壁は突然現れました。まず、error内容をグーグル先生に伝えました。
そうすると php.ini の内容を変えましょうという助言をいただきました。【PHP】composer require時にPHP Fatal error: Allowed memory size of XXX bytes exhaustedが表示されたら
記事を拝読しました。
記事に、メモリ上限値無制限とあって、怖いなと感じて素直に行動しませんでした。
ここが良くなかったです。自ら時間を捨てる結果となりました。# まずは、memory_limitの設定値を確認してみました (記事通りの結果) % php -r "echo ini_get('memory_limit').PHP_EOL;" 128M# php.iniの場所を確認しました (MacOS) % php -i | grep php.ini Configuration File (php.ini) Path => /etc Loaded Configuration File => /etc/php.ini先程のerror文の中に
PHP Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes)とあったので
php.iniを見て疑問に思いました。php.inimemory_limit = 128M # 256M や 512Mにしてみましたが…4096 byteとあったので
0.004MBくらいじゃん!128M(MB?)で何がいけないの?
と思っていました。数値を適当に上げましたが
同じerror文が出ました。ファイルの場所や参照している何かが違うのか?と
1時間以上、調べまくりました。しかし、どうにもならないので
希望の光もなく、見覚えのある数字にしてみました。php.inimemory_limit = 4096M
としました。
そしたら、成功しました。
キモチェ〜〜〜〜というか
ファ?!という感じでした。 完数時間後、この記事を書いている際
error文を翻訳してみました。
1610612736バイトの許容メモリサイズが使い果たされました
(4096バイトを割り当てようとしました)とのこと。
コア php.ini ディレクティブに関する説明 ¶リソース制限
公式サイトを確認してもよくわからなかったので
4096MのMってMemoryのMなのかな〜
そんなことを考えながら
別件のメールサーバーいじってたら、
6時間も過ぎてしまいました。ちょっとイジるつもりが詰まってしまって…
何もできていない、開発しないとッ!!
緩い日報のようになってしまいました。
今回の投稿で感じたことは
画像より文字の方がいいなと感じたので
スクリーンショットではなくて
これからは、メモなどにコピペしておこうと思います。Markdown 覚えないと…
以上
- 投稿日:2020-10-10T16:38:26+09:00
Mass Assignment とは(laravel)
Webアプリケーションのアクティブなレコードパターンが悪用され、パスワード、許可された権限、または管理者のステータスなど、ユーザーが通常はアクセスを許可すべきでないデータ項目を変更するコンピュータの脆弱性です。
以下引用
https://csword.net/2018/07/04/mass_assignment_vulnerability_%E3%81%A8%E3%81%AF/Web上から入力されてきた値を制限することで、不正なパラメータを防ぐ仕組みらしいです
laravelではEloquentで Mass Assignmentの対策されているとのこと
->パラメータ(パソコンの動作を変える為の外部から与える設定値)
->Eloquent(Laravelで提供されているデータ操作の為の機能)
Eloquentについての詳細↓
https://readouble.com/laravel/6.x/ja/eloquent.html
https://laravel.com/docs/6.x/eloquent#mass-assignment
- 投稿日:2020-10-10T15:30:02+09:00
Moodle 3.9 マニュアル - 新しいコースを追加する
内容
1 コースの追加
2 コースの削除
3 コースの並べ替え
4 コースリクエスト
4.1 コースリクエストを自動的に承認する
5 一括コース作成
6 コーステンプレート
6.1 既存のコースをテンプレートとして使用する
6.1.1 方法1
6.1.2 方法2
6.2 バックアップコースをテンプレートとして使用する
6.3 コーステンプレートプラグインの使用
7 関連項目
7.1 スクリーンキャスト
7.2 フォーラムディスカッション1 コースの追加
デフォルトでは、通常の教師は新しいコースを追加できません。 Moodleに新しいコースを追加するには、管理者、コース作成者、またはマネージャのいずれかの権限が必要です。コースを追加するには:
[サイト管理]リンクから、[コース]> [コースとカテゴリの管理]をクリックします
コースを希望するカテゴリをクリックします。詳細については、コースカテゴリを参照してください
「新しいコース」リンクをクリックします
コース設定を入力し、「保存して戻る」を選択してコースに戻るか、「保存して表示」を選択して次の画面に進みます。次の画面で、[保存して表示]を選択した場合は、コースに割り当てる生徒/教師を選択します。
2 コースの削除
教師はコースを削除できません。管理者(つまり、ケーパビリティ:moodle/course:deleteが許可されている役割を持つユーザー)はコースを削除でき、コース作成者は自分で作成したコースを削除できますが、コースを作成してから24時間以内です。これは、誤って作成されたコースを管理者に依頼することなく削除できるようにするためです。
管理者はいつでもコースを削除できます。
コースを削除するには(管理者またはマネージャーとして):
1.[サイト管理]リンクから、[コース]> [コースとカテゴリの管理]をクリックします
2. コースのカテゴリをクリックし、右側の画面でコースをクリックします。
3. [削除]リンクをクリックします。次の方法で複数のコースを削除できます。
・新しい(一時的な)カテゴリを作成します。 「削除予定」という名前を付けることができます。
・「削除予定」コースを選択して、そのカテゴリ(「削除予定」)に移動します。
・カテゴリを削除し(「削除予定」)、「すべて削除-元に戻せません」を選択します。コース作成者が作成したコースを削除するためのユーザーインターフェイスはありません。ただし、コースのURLをhttp://yourmoodlesite.net/course/view.php?id=N から http://yourmoodlesite.net/course/delete.php?id=N に編集することでこれを行うことができます(置換「表示」と「削除」)。
3 コースの並べ替え
コースは、管理者または管理者が[管理]> [サイト管理]> [コース]> [コースとカテゴリの管理]から並べ替えることができます。
コースは、昇順/降順および作成された時間で並べ替えることができます。
4 コースリクエスト
コースリクエスト機能は、管理者が[管理]> [サイト管理]> [コース]> [コースリクエスト]で有効にできます。
管理者は、コースリクエストのデフォルトのカテゴリ、ユーザーがコースをリクエストするときにカテゴリを選択できるかどうか、コースリクエストの通知を受け取ることができるユーザーを設定できます(ケーパビリティmoodle/site:approvecourseを持つユーザーのリストから)。
「新しいコースをリクエスト」ボタンが「すべてのコース」ページに表示されます。 [すべてのコース]ページには、コースブロックのリンクからアクセスできます。または、新しいコースページ https://yourmoodlesite.org/course/request.php をリクエストするためのリンクを(HTMLブロックやページリソースなどで)作成することもできます。
コースリクエストが有効になっている場合、デフォルトでは、認証されたすべてのユーザーがコースリクエストを行うことができます。コースリクエストを行うことができるユーザーを制限する方法の詳細については、コースリクエストの役割を参照してください。
4.1 コースリクエストを自動的に承認する
プラグインの自動承認により、Learn Moodle Basics MOOCのように、コースを自動的に承認することができます。
5 一括コース作成
コースを一括作成してコーステンプレートを使用する方法の詳細については、コースのアップロードを参照してください。
- [管理]> [サイト管理]> [コース]> [コースのアップロード]に移動します。
- ドラッグアンドドロップするか、ボタンを使用してファイルピッカーから選択することにより、CSVファイルをアップロードします。
- インポートオプションを選択し、[プレビュー]をクリックします。設定に問題がない場合は、[アップロード]をクリックします。
6 コーステンプレート
[管理]> [サイト管理]> [コース]> [コースのアップロード]から、将来のコースのテンプレートとして使用するコースと設定を指定できます。
新しいコースのデフォルトブロックは、config.phpで構成することもできます。
6.1 既存のコースをテンプレートとして使用する
まず、テンプレートとして使用したいコースをMoodleで作成または検索し、その短縮名をメモします。テンプレートコースには、たとえば、一貫性を保つためにサイト全体で使用される共通の見出し、セクションの概要、またはポリシー契約が含まれる場合があります。
コースの内容のみが復元されることに注意してください。その設定ではありません。設定は別途追加されます。
6.1.1 方法1
csvファイルを作成します。受け入れられるフィールドについては、コースのアップロードを参照してください。
[サイト管理]リンクから、[コース]> [コースのアップロード]をクリックして、ファイルを追加します。
プレビューして、[コースプロセス]まで下にスクロールします。 [アップロード後にこのコースから復元する]ボックスに、テンプレートコースの短縮名を追加します。
同じテンプレートを使用して複数のコースを作成していて、それらすべてに同じ設定を持たせたい場合は、デフォルトのコース値でこれらを指定できます。
csvファイルをアップロードします。6.1.2 方法2
作成したcsvファイルですべての設定と選択したテンプレートコースを指定できます。選択したテンプレートコースのフィールドとしてtemplatecourseを使用し、コースの短縮名を使用します。含めることができるフィールドの完全なリストについては、コースのアップロードを参照してください。
テンプレートとして使用するコースを復元するためのcsvファイルの例を次に示します。たとえば、テーマ、言語、登録方法、生徒と教師の別名を指定します。shortname,fullname,category,newsitems,theme,lang,format,enrolment_1,enrolment_1_role,role_student,role_teacher,templatecourse
Knit,Knitting,2,0,afterburner,fr,topics,manual,student,NewKnitter,MasterKnitter,OMT[管理]> [サイト管理]> [コース]> [コースのアップロード]に移動し、csvファイルを追加します
6.2 バックアップコースをテンプレートとして使用する
[サイト管理]> [管理]> [コース]> [コースのアップロード]でcsvファイルを使用してコースを作成するプロセス中に、テンプレートとして使用するバックアップファイルを指定してアップロードすることができます。
csvファイルを作成します。受け入れられるフィールドについては、コースのアップロードを参照してください。
[サイト管理]> [管理]> [コース]> [コースのアップロード]から、ファイルを追加します。
プレビューして、[コースプロセス]まで下にスクロールします。 [アップロード後にこのファイルから復元する]ボックスに、テンプレートとして使用するバックアップファイルを追加します。
同じテンプレートを使用して複数のコースを作成していて、それらすべてに同じ設定を持たせたい場合は、「デフォルトのコース値」セクションでこれらを指定できます。
または、csvファイルでコース設定(形式、登録方法、言語など)を指定することもできます。この方法は、同じテンプレートを使用する異なるコースに異なる設定を持たせたい場合に役立ちます。
csvファイルをアップロードします。6.3 コーステンプレートプラグインの使用
emeneoのコーステンプレートプラグインを使用すると、テンプレートベースのコースを非常に簡単に作成できます。詳細とプラグインパッケージはここにあります:https://moodle.org/plugins/local_course_templates
7 関連項目
7.1 スクリーンキャスト
7.2 フォーラムディスカッション
- 投稿日:2020-10-10T14:40:21+09:00
配列のキーが連番でないときに、値を挿入する方法
配列のキーが連番じゃない
配列のキーが連番じゃなくて良い感じに値を入れてあげたい時。
$array = array(0=>'a', 1=>'b', 3=>'d'); $array += array_fill(0, max(array_keys($array)), 'c'); ksort($array);実行結果
array(4) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(1) "c" [3]=> string(1) "d" }こんな風に自然な配列の形に整形できます。
$array += array_fill(0, max(array_keys($array)), '空です。');実行結果
array(4) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> string(12) "空です。" [3]=> string(1) "d" }こんな風にすれば抜けていた配列の判別も簡単にできます。
- 投稿日:2020-10-10T14:26:32+09:00
【PHP】TSVファイルを配列に変換する
webサイトなどでカテゴリを設定・呼び出す際に必要なときがちょいちょいあるかと思います。
環境
PHP 7.3.18
tsvデータ例
スプレッドシートなどで下記のような表を作成
※一番左の列に項目を、右に列でデータを追加していく~/config/member.tsv
name 田中 斎藤 id tanaka saito tanaka@~ saito@~ age 24 31 from tokyo saitama 実装
hash.phptoHash(~/config/member.tsv,"\t"); function toHash($filePath, $format = false, $first = false) { //formatはtsvしか許可しない if (!($format == "\t")) { return false; } //ファイルの存在判定 if (!file_exists($filePath)) { return false; } $fl = file($filePath); $data = array(); foreach ($fl as $k => $v) { $column = explode($format, $v); if ($k == 0) { foreach ($column as $i => $j) { $title[$i] = trim($j); } if ($first == false) { continue; } } foreach ($column as $i => $j) { if ($i == 0) { $key = $j; } else { $data[$key][$title[$i]] = trim($j); } } } return $data; }結果
array(4) { ["id"]=> array(2) { ["田中"]=> string(6) "tanaka" ["斎藤"]=> string(5) "saito" } ["email"]=> array(2) { ["田中"]=> string(6) "tanaka" ["斎藤"]=> string(5) "saito" } ["age"]=> array(2) { ["田中"]=> string(2) "24" ["斎藤"]=> string(2) "31" } ["from"]=> array(2) { ["田中"]=> string(5) "tokyo" ["斎藤"]=> string(7) "saitama" } }各行ごとに配列で返ってきます。
- 投稿日:2020-10-10T12:18:11+09:00
Moodle 3.9 マニュアル - カテゴリ:コース
カテゴリ:コース
コース機能に関するドキュメントページのインデックス。
サブカテゴリこのカテゴリには、合計3つのうち、次の3つのサブカテゴリがあります。
CF
R
カテゴリ「コース」のページ
合計77ページのうち、次の77ページがこのカテゴリに含まれます。
あ
き
こ
し
て
ふ
ゆ
り
A
Activities 日本語訳:アクティビティ
Activity chooser
Adding a new courseB
Blocks 日本語訳:ブロック
Buttons course formatC
Capabilities/block/course list:addinstance
Capabilities/block/course summary:addinstance
Capabilities/block/recent activity:addinstance
Capabilities/block/social activities:addinstance
Capabilities/enrol/category:config
Capabilities/enrol/database:config
Capabilities/enrol/guest:config
Capabilities/enrol/imsenterprise:config
Capabilities/enrol/manual:enrol
Capabilities/moodle/category:create
Capabilities/moodle/category:delete
Capabilities/moodle/category:manage
Capabilities/moodle/category:update
Capabilities/moodle/category:viewhiddencategories
Capabilities/moodle/course:activityvisibility
Capabilities/moodle/course:changecategory
Capabilities/moodle/course:changefullname
Capabilities/moodle/course:changeidnumber
Capabilities/moodle/course:changelockedcustomfields
Capabilities/moodle/course:changeshortname
Capabilities/moodle/course:changesummary
Capabilities/moodle/course:configurecustomfields
Capabilities/moodle/course:create
Capabilities/moodle/course:delete
Capabilities/moodle/course:ignoreavailabilityrestrictions
Capabilities/moodle/course:ignorefilesizelimits
Capabilities/moodle/course:manageactivities
Capabilities/moodle/course:managefiles
Capabilities/moodle/course:managemetacourse
Capabilities/moodle/course:movesections
Capabilities/moodle/course:recommendactivity
Capabilities/moodle/course:renameroles
Capabilities/moodle/course:request
Capabilities/moodle/course:sectionvisibility
Capabilities/moodle/course:setcurrentsection
Capabilities/moodle/course:update
Capabilities/moodle/course:viewhiddencourses
Capabilities/moodle/course:viewhiddensections
Capabilities/moodle/course:viewhiddenuserfields
Capabilities/moodle/course:viewparticipants
Capabilities/moodle/course:visibility
Capabilities/moodle/rating:rate
Capabilities/moodle/rating:view
Capabilities/moodle/rating:viewall
Capabilities/moodle/rating:viewany
Capabilities/moodle/site:import
Collapsed Topics course format
Course backup
Course categories
Course enrolment
Course FAQ
Course formats
Course homepage 日本語訳:コースのホームページ
Course list
Course overview (legacy)
Course relative dates
Course restore
Course settings
Courses 日本語訳:コース
Courses blockD
E
G
Grouping users 日本語訳:ユーザーのグループ化
Q
R
Recent activity block
Resources 日本語訳:リソース
Restrict access
Reusing activitiesS
T
Teacher quick guide 日本語訳:教師用クイックガイド
Tracking progress 日本語訳:進捗状況の追跡U
- 投稿日:2020-10-10T10:18:20+09:00
【Laravel】Collectionのpluckメソッド
はじめに
今回もLaravelのCollectionのメソッドについてまとめたいと思います。
pluckメソッドで指定したキーの配列を取得する
例えば、nameの値のみをを取得する場合下記のような実装になります。
$collection = collect([ ['id' => 1, 'name' => '山田', 'age' => 18], ['id' => 2, 'name' => '佐藤', 'age' => 38], ['id' => 3, 'name' => '小林', 'age' => 25], ]); $names = $collection->pluck('name'); print_r($names->toArray());実行結果は下記になります。
Array ( [0] => 山田 [1] => 佐藤 [2] => 小林 )
pluck('id', 'name')
のようにidとnameなど複数指定するとその指定した値のみを配列にして取得することもできます。おわりに
いかがでしたでしょうか。
指定したキーの配列を取得をして試してみてください。
- 投稿日:2020-10-10T09:35:28+09:00
Moodle 3.9 マニュアル - アクティビティの再利用
アクティビティの再利用
コースまたはコースの一部を再利用するには、いくつかの方法があります。それらのほとんどは、バックアップと復元のプロセスに関係するか、類似しています。
コースデータのインポート-教師が教える別のコースに移動して、要素を現在のコースにインポートできるようにします。 1つのプロセスであることを除いて、バックアップと復元に似ています。
コースのリセット-基本的に、現在のコースのすべてのアクティビティをクリーンアップします。
リソース/アクティビティを複製する-アクティビティまたはリソースの[設定の編集]メニューの[複製]オプションを使用して、別のコピーを作成します。関連項目
コースのバックアップ-コースの全部または一部を取得し、現在のサイトまたは別のMoodleサイトに複製する従来の方法(復元とともに)。
コースの復元-コースのバックアップの全部または一部を取得して、既存のコースに追加したり、新しいコースを作成したりできます。
共有カート-コース間でアクティビティを簡単に共有できるプラグイン。
- 投稿日:2020-10-10T03:27:37+09:00
Moodle 3.9 マニュアル - 進捗状況の追跡
Moodleで生徒の進歩を追跡するいくつかの方法があります。このページでは、コースで教師が利用できるものの概要を説明します。
内容
1 成績
2 コンピテンシー
3 アクティビティの完了
4 コース修了
5 バッジ
6 コースレポート
7 分析1 成績
すべてのコースには独自の成績表があり、コース管理>成績表の設定からアクセスできます。課題やクイズなどの一部のアクティビティでは、成績がこの成績表に返送されます。教師が成績表に直接成績を入力することも可能です。
成績表のスクリーンキャスト
2 コンピテンシー
コンピテンシーは、特定の主題関連スキルにおける学習者の理解または習熟度のレベルを表します。コンピテンシーベースの学習またはスキルベースの学習としても知られるコンピテンシーベースの教育(CBE)は、学生がこれらの能力を実証する評価および評価のシステムを指します。
3 アクティビティの完了
管理者がコース設定でアクティビティの完了を有効にしている場合、教師はコース項目ごとに、完了として登録する方法を指定できます。アクティビティに対してチェックマーク/チェックマークが表示されます。生徒は手動で完了のマークを付けるか、生徒が指定された基準を満たしたときにアイテムが自動的に完了として登録されます。これらは、リソースの表示、課題の送信、フォーラムへの投稿、またはその他の条件である可能性があります。教師は、コース管理>レポート>アクティビティ完了のアクティビティ完了レポートで、誰が何を完了したかについての概要を確認できます。
4コース修了
アクティビティ完了の延長として、コース完了を有効にすると、指定された基準に従って手動または自動で、コースを正式に終了としてマークすることができます。
コース完了ステータスブロックが追加されている場合、学生はコース中の進捗状況を確認できます。教師は、[コース管理]> [レポート]> [コース完了]から、コース完了に向けた学生の全体的な進捗状況を確認できます。5 バッジ
バッジは、手動で、またはコースのアクティビティ完了設定を使用して授与することができ、学生のやる気を引き出すための一般的な方法です。学生は、コースのさまざまな段階で、さまざまなレベルの進歩に対してバッジを授与される場合があります。
6 コースレポート
教師は、生徒の進捗状況を追跡するのに役立つ多数のコースレポートをコースで利用できます。上記のアクティビティレポートおよびコース完了レポート(これらの設定が有効になっている場合にのみ使用可能)に加えて、アクティビティレポート、参加レポート、および一般的なコースログもあります。
7 分析
Moodle 3.4以降の分析機能は、リスクのある学習者をサポートするための詳細な説明的および規範的なレポートを提供します。
- 投稿日:2020-10-10T02:31:23+09:00
Moodle 3.9 マニュアル - 成績
グレーディングクイックガイド
採点者レポート-成績表または「採点者レポート」は、すべてのコースの成績が照合される場所です。
成績設定-成績表がユーザーに表示される方法のデフォルトと設定を設定するためのオプション。
成績の管理
グレード非表示
グレードアイテム
グレードレター
グレードカテゴリ
グレード計算
学年歴
はかり
結果
グレードのインポート
グレードのエクスポート
高度なグレーディング方法
ルーブリック
マーキングガイド
成績に関するFAQ