[{"data":1,"prerenderedAt":3937},["ShallowReactive",2],{"articles-page-6":3},{"count":4,"content":5},63,[6,1153,1298,1389,1485,2053,2495,2603,2715,2965],{"id":7,"title":8,"body":9,"category":1140,"createdAt":1142,"description":8,"extension":1143,"index":1144,"meta":1145,"navigation":193,"path":1146,"publish":193,"seo":1147,"series":1144,"seriesTitle":1144,"stem":1148,"tag":1149,"thumbnail":1151,"updatedAt":1144,"__hash__":1152},"articles\u002Farticles\u002Frecent-card-css.md","イマドキな丸角・正方形・画像カードデザインのレイアウトをCSSで実現させる。",{"type":10,"value":11,"toc":1137},"minimark",[12,24,28,31,56,65,70,73,1134],[13,14,15,16,23],"p",{},"こんにちは jun です。カードデザイン（参考：",[17,18,22],"a",{"href":19,"rel":20},"http:\u002F\u002Fpinterest.jp\u002F",[21],"nofollow","pinterest","）に近いコンポーネントを作った時に意外と詰まって時間かけてしまったので忘備録として残します。実際に↓に作りました。（レスポンシブでない）",[25,26,27],"h2",{"id":27},"コンポーネント要件",[13,29,30],{},"コンポーネントの要件としては以下の通りです。",[32,33,34,38,41,44,47,50,53],"ul",{},[35,36,37],"li",{},"正方形",[35,39,40],{},"丸角",[35,42,43],{},"画像が表示される。",[35,45,46],{},"PCでは３列、タブレットは２列、スマホは１列を維持する",[35,48,49],{},"端（コンテナー の境界）はくっつける。",[35,51,52],{},"1remほど隙間は開ける",[35,54,55],{},"ホバー時に背景画像がぐわっ！と迫ってくる",[13,57,58,59,64],{},"完成したものは",[17,60,63],{"href":61,"rel":62},"http:\u002F\u002Fapp.jun-app.com\u002Fcard-css\u002F",[21],"こちら","のページに置いてあります。画像は以下の通りです。",[66,67],"image-render",{":src":68,":width":69},"'_mix\u002Fsch-2021-04-25 22.19.26.png'","'100%'",[13,71,72],{},"コードはこちら",[74,75,80],"pre",{"className":76,"code":77,"language":78,"meta":79,"style":79},"language-html shiki shiki-themes material-theme-ocean","\u003Cstyle>\n    *{\n        box-sizing: border-box;\n    }\n    html{\n        font-size: 10px;\n    }\n    .p-the-container{\n        width: 100%;\n    }\n\n    .p-the-wrapper{\n        display: flex;\n        flex-wrap: wrap;\n        margin: 0 -1rem;         \u002F* p-img-card の paddingと同じ *\u002F\n    }\n\n    .p-img-card{\n        width: 33.3%;\n        width: 33.3%;\n\n        padding: 1rem;          \u002F* 隙間の太さになる *\u002F\n        margin-bottom: 2rem;    \u002F* 下側の隙間の太さ *\u002F\n\n        position: relative;\n        overflow: hidden;\n        cursor: pointer;\n    }\n\n    .p-img-card::before{\n        content: '';\n        display: block;\n        padding-top: 100%;      \u002F*正方形にするおまじない*\u002F\n    }\n\n    .p-img-card-wrapper{\n        display: block;\n        width: calc(100% - 2rem);  \u002F* カードのラッパーは隙間paddingの分だけ小さくする *\u002F\n        height: calc(100% - 2rem); \u002F* カードのラッパーは隙間paddingの分だけ小さくする *\u002F\n        position: absolute;\n        top: 1rem;　　　　　　　　　　\u002F* 位置もカードのpaddingの分だけ下げる *\u002F\n        overflow: hidden;\n        border-radius: 35px;\n        box-shadow: 0px 0px 8px -3px rgba(0, 0, 0, 0.6);\n    }\n\n    .p-img-card .c-img{\n        width: 100%;\n        height: 100%;\n        background-position: center;\n        background-repeat: no-repeat;\n        background-size: cover;\n        transform: scale(1);\n        transition: transform 0.5s;\n    }\n\n    .p-img-card:hover .c-img{\n        transform: scale(1.2);\n        transition: transform 0.5s;\n        animation: min-and-big 3s;\n    }\n\n    .img-a .c-img{background-image: url(画像のパス);}\n    .img-b .c-img{background-image: url(画像のパス);}\n\n\u003C\u002Fstyle>\n\n\u003Cdiv class=\"p-the-container\">\n    \u003Cdiv class=\"p-the-wrapper\">\n        \u003Cdiv class=\"p-img-card img-a\">\n            \u003Cdiv class=\"p-img-card-wrapper\">\n                \u003Cdiv class=\"c-img\">\u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"p-img-card img-b\">\n            \u003Cdiv class=\"p-img-card-wrapper\">\n                \u003Cdiv class=\"c-img\">\u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n　　　　　...\n    \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n","html","",[81,82,83,99,109,126,132,140,154,159,170,183,188,195,205,218,231,252,257,262,272,284,295,300,316,332,337,350,363,376,381,386,402,415,427,442,447,452,462,473,501,524,536,551,562,575,619,624,629,644,655,666,679,692,705,723,739,744,749,767,783,796,812,817,822,853,879,884,894,899,923,943,964,984,1009,1019,1029,1049,1068,1091,1100,1109,1115,1125],"code",{"__ignoreMap":79},[84,85,88,92,96],"span",{"class":86,"line":87},"line",1,[84,89,91],{"class":90},"sAklC","\u003C",[84,93,95],{"class":94},"s-wAU","style",[84,97,98],{"class":90},">\n",[84,100,102,106],{"class":86,"line":101},2,[84,103,105],{"class":104},"s5Dmg","    *",[84,107,108],{"class":90},"{\n",[84,110,112,116,119,123],{"class":86,"line":111},3,[84,113,115],{"class":114},"s6YsC","        box-sizing",[84,117,118],{"class":90},":",[84,120,122],{"class":121},"s0W1g"," border-box",[84,124,125],{"class":90},";\n",[84,127,129],{"class":86,"line":128},4,[84,130,131],{"class":90},"    }\n",[84,133,135,138],{"class":86,"line":134},5,[84,136,137],{"class":104},"    html",[84,139,108],{"class":90},[84,141,143,146,148,152],{"class":86,"line":142},6,[84,144,145],{"class":114},"        font-size",[84,147,118],{"class":90},[84,149,151],{"class":150},"sx098"," 10px",[84,153,125],{"class":90},[84,155,157],{"class":86,"line":156},7,[84,158,131],{"class":90},[84,160,162,165,168],{"class":86,"line":161},8,[84,163,164],{"class":90},"    .",[84,166,167],{"class":104},"p-the-container",[84,169,108],{"class":90},[84,171,173,176,178,181],{"class":86,"line":172},9,[84,174,175],{"class":114},"        width",[84,177,118],{"class":90},[84,179,180],{"class":150}," 100%",[84,182,125],{"class":90},[84,184,186],{"class":86,"line":185},10,[84,187,131],{"class":90},[84,189,191],{"class":86,"line":190},11,[84,192,194],{"emptyLinePlaceholder":193},true,"\n",[84,196,198,200,203],{"class":86,"line":197},12,[84,199,164],{"class":90},[84,201,202],{"class":104},"p-the-wrapper",[84,204,108],{"class":90},[84,206,208,211,213,216],{"class":86,"line":207},13,[84,209,210],{"class":114},"        display",[84,212,118],{"class":90},[84,214,215],{"class":121}," flex",[84,217,125],{"class":90},[84,219,221,224,226,229],{"class":86,"line":220},14,[84,222,223],{"class":114},"        flex-wrap",[84,225,118],{"class":90},[84,227,228],{"class":121}," wrap",[84,230,125],{"class":90},[84,232,234,237,239,242,245,248],{"class":86,"line":233},15,[84,235,236],{"class":114},"        margin",[84,238,118],{"class":90},[84,240,241],{"class":150}," 0",[84,243,244],{"class":150}," -1rem",[84,246,247],{"class":90},";",[84,249,251],{"class":250},"sC9rS","         \u002F* p-img-card の paddingと同じ *\u002F\n",[84,253,255],{"class":86,"line":254},16,[84,256,131],{"class":90},[84,258,260],{"class":86,"line":259},17,[84,261,194],{"emptyLinePlaceholder":193},[84,263,265,267,270],{"class":86,"line":264},18,[84,266,164],{"class":90},[84,268,269],{"class":104},"p-img-card",[84,271,108],{"class":90},[84,273,275,277,279,282],{"class":86,"line":274},19,[84,276,175],{"class":114},[84,278,118],{"class":90},[84,280,281],{"class":150}," 33.3%",[84,283,125],{"class":90},[84,285,287,289,291,293],{"class":86,"line":286},20,[84,288,175],{"class":114},[84,290,118],{"class":90},[84,292,281],{"class":150},[84,294,125],{"class":90},[84,296,298],{"class":86,"line":297},21,[84,299,194],{"emptyLinePlaceholder":193},[84,301,303,306,308,311,313],{"class":86,"line":302},22,[84,304,305],{"class":114},"        padding",[84,307,118],{"class":90},[84,309,310],{"class":150}," 1rem",[84,312,247],{"class":90},[84,314,315],{"class":250},"          \u002F* 隙間の太さになる *\u002F\n",[84,317,319,322,324,327,329],{"class":86,"line":318},23,[84,320,321],{"class":114},"        margin-bottom",[84,323,118],{"class":90},[84,325,326],{"class":150}," 2rem",[84,328,247],{"class":90},[84,330,331],{"class":250},"    \u002F* 下側の隙間の太さ *\u002F\n",[84,333,335],{"class":86,"line":334},24,[84,336,194],{"emptyLinePlaceholder":193},[84,338,340,343,345,348],{"class":86,"line":339},25,[84,341,342],{"class":114},"        position",[84,344,118],{"class":90},[84,346,347],{"class":121}," relative",[84,349,125],{"class":90},[84,351,353,356,358,361],{"class":86,"line":352},26,[84,354,355],{"class":114},"        overflow",[84,357,118],{"class":90},[84,359,360],{"class":121}," hidden",[84,362,125],{"class":90},[84,364,366,369,371,374],{"class":86,"line":365},27,[84,367,368],{"class":114},"        cursor",[84,370,118],{"class":90},[84,372,373],{"class":121}," pointer",[84,375,125],{"class":90},[84,377,379],{"class":86,"line":378},28,[84,380,131],{"class":90},[84,382,384],{"class":86,"line":383},29,[84,385,194],{"emptyLinePlaceholder":193},[84,387,389,391,393,396,400],{"class":86,"line":388},30,[84,390,164],{"class":90},[84,392,269],{"class":104},[84,394,395],{"class":90},"::",[84,397,399],{"class":398},"sJ14y","before",[84,401,108],{"class":90},[84,403,405,408,410,413],{"class":86,"line":404},31,[84,406,407],{"class":114},"        content",[84,409,118],{"class":90},[84,411,412],{"class":90}," ''",[84,414,125],{"class":90},[84,416,418,420,422,425],{"class":86,"line":417},32,[84,419,210],{"class":114},[84,421,118],{"class":90},[84,423,424],{"class":121}," block",[84,426,125],{"class":90},[84,428,430,433,435,437,439],{"class":86,"line":429},33,[84,431,432],{"class":114},"        padding-top",[84,434,118],{"class":90},[84,436,180],{"class":150},[84,438,247],{"class":90},[84,440,441],{"class":250},"      \u002F*正方形にするおまじない*\u002F\n",[84,443,445],{"class":86,"line":444},34,[84,446,131],{"class":90},[84,448,450],{"class":86,"line":449},35,[84,451,194],{"emptyLinePlaceholder":193},[84,453,455,457,460],{"class":86,"line":454},36,[84,456,164],{"class":90},[84,458,459],{"class":104},"p-img-card-wrapper",[84,461,108],{"class":90},[84,463,465,467,469,471],{"class":86,"line":464},37,[84,466,210],{"class":114},[84,468,118],{"class":90},[84,470,424],{"class":121},[84,472,125],{"class":90},[84,474,476,478,480,484,487,490,493,495,498],{"class":86,"line":475},38,[84,477,175],{"class":114},[84,479,118],{"class":90},[84,481,483],{"class":482},"sdLwU"," calc",[84,485,486],{"class":90},"(",[84,488,489],{"class":150},"100%",[84,491,492],{"class":90}," -",[84,494,326],{"class":150},[84,496,497],{"class":90},");",[84,499,500],{"class":250},"  \u002F* カードのラッパーは隙間paddingの分だけ小さくする *\u002F\n",[84,502,504,507,509,511,513,515,517,519,521],{"class":86,"line":503},39,[84,505,506],{"class":114},"        height",[84,508,118],{"class":90},[84,510,483],{"class":482},[84,512,486],{"class":90},[84,514,489],{"class":150},[84,516,492],{"class":90},[84,518,326],{"class":150},[84,520,497],{"class":90},[84,522,523],{"class":250}," \u002F* カードのラッパーは隙間paddingの分だけ小さくする *\u002F\n",[84,525,527,529,531,534],{"class":86,"line":526},40,[84,528,342],{"class":114},[84,530,118],{"class":90},[84,532,533],{"class":121}," absolute",[84,535,125],{"class":90},[84,537,539,542,544,546,548],{"class":86,"line":538},41,[84,540,541],{"class":114},"        top",[84,543,118],{"class":90},[84,545,310],{"class":150},[84,547,247],{"class":90},[84,549,550],{"class":250},"　　　　　　　　　　\u002F* 位置もカードのpaddingの分だけ下げる *\u002F\n",[84,552,554,556,558,560],{"class":86,"line":553},42,[84,555,355],{"class":114},[84,557,118],{"class":90},[84,559,360],{"class":121},[84,561,125],{"class":90},[84,563,565,568,570,573],{"class":86,"line":564},43,[84,566,567],{"class":114},"        border-radius",[84,569,118],{"class":90},[84,571,572],{"class":150}," 35px",[84,574,125],{"class":90},[84,576,578,581,583,586,588,591,594,597,599,602,605,607,609,611,613,616],{"class":86,"line":577},44,[84,579,580],{"class":114},"        box-shadow",[84,582,118],{"class":90},[84,584,585],{"class":150}," 0px",[84,587,585],{"class":150},[84,589,590],{"class":150}," 8px",[84,592,593],{"class":150}," -3px",[84,595,596],{"class":482}," rgba",[84,598,486],{"class":90},[84,600,601],{"class":150},"0",[84,603,604],{"class":90},",",[84,606,241],{"class":150},[84,608,604],{"class":90},[84,610,241],{"class":150},[84,612,604],{"class":90},[84,614,615],{"class":150}," 0.6",[84,617,618],{"class":90},");\n",[84,620,622],{"class":86,"line":621},45,[84,623,131],{"class":90},[84,625,627],{"class":86,"line":626},46,[84,628,194],{"emptyLinePlaceholder":193},[84,630,632,634,636,639,642],{"class":86,"line":631},47,[84,633,164],{"class":90},[84,635,269],{"class":104},[84,637,638],{"class":90}," .",[84,640,641],{"class":104},"c-img",[84,643,108],{"class":90},[84,645,647,649,651,653],{"class":86,"line":646},48,[84,648,175],{"class":114},[84,650,118],{"class":90},[84,652,180],{"class":150},[84,654,125],{"class":90},[84,656,658,660,662,664],{"class":86,"line":657},49,[84,659,506],{"class":114},[84,661,118],{"class":90},[84,663,180],{"class":150},[84,665,125],{"class":90},[84,667,669,672,674,677],{"class":86,"line":668},50,[84,670,671],{"class":114},"        background-position",[84,673,118],{"class":90},[84,675,676],{"class":121}," center",[84,678,125],{"class":90},[84,680,682,685,687,690],{"class":86,"line":681},51,[84,683,684],{"class":114},"        background-repeat",[84,686,118],{"class":90},[84,688,689],{"class":121}," no-repeat",[84,691,125],{"class":90},[84,693,695,698,700,703],{"class":86,"line":694},52,[84,696,697],{"class":114},"        background-size",[84,699,118],{"class":90},[84,701,702],{"class":121}," cover",[84,704,125],{"class":90},[84,706,708,711,713,716,718,721],{"class":86,"line":707},53,[84,709,710],{"class":114},"        transform",[84,712,118],{"class":90},[84,714,715],{"class":482}," scale",[84,717,486],{"class":90},[84,719,720],{"class":150},"1",[84,722,618],{"class":90},[84,724,726,729,731,734,737],{"class":86,"line":725},54,[84,727,728],{"class":114},"        transition",[84,730,118],{"class":90},[84,732,733],{"class":121}," transform ",[84,735,736],{"class":150},"0.5s",[84,738,125],{"class":90},[84,740,742],{"class":86,"line":741},55,[84,743,131],{"class":90},[84,745,747],{"class":86,"line":746},56,[84,748,194],{"emptyLinePlaceholder":193},[84,750,752,754,756,758,761,763,765],{"class":86,"line":751},57,[84,753,164],{"class":90},[84,755,269],{"class":104},[84,757,118],{"class":90},[84,759,760],{"class":398},"hover",[84,762,638],{"class":90},[84,764,641],{"class":104},[84,766,108],{"class":90},[84,768,770,772,774,776,778,781],{"class":86,"line":769},58,[84,771,710],{"class":114},[84,773,118],{"class":90},[84,775,715],{"class":482},[84,777,486],{"class":90},[84,779,780],{"class":150},"1.2",[84,782,618],{"class":90},[84,784,786,788,790,792,794],{"class":86,"line":785},59,[84,787,728],{"class":114},[84,789,118],{"class":90},[84,791,733],{"class":121},[84,793,736],{"class":150},[84,795,125],{"class":90},[84,797,799,802,804,807,810],{"class":86,"line":798},60,[84,800,801],{"class":114},"        animation",[84,803,118],{"class":90},[84,805,806],{"class":121}," min-and-big ",[84,808,809],{"class":150},"3s",[84,811,125],{"class":90},[84,813,815],{"class":86,"line":814},61,[84,816,131],{"class":90},[84,818,820],{"class":86,"line":819},62,[84,821,194],{"emptyLinePlaceholder":193},[84,823,824,826,829,831,833,836,839,841,844,846,850],{"class":86,"line":4},[84,825,164],{"class":90},[84,827,828],{"class":104},"img-a",[84,830,638],{"class":90},[84,832,641],{"class":104},[84,834,835],{"class":90},"{",[84,837,838],{"class":114},"background-image",[84,840,118],{"class":90},[84,842,843],{"class":482}," url",[84,845,486],{"class":90},[84,847,849],{"class":848},"s7ZW3","画像のパス",[84,851,852],{"class":90},");}\n",[84,854,856,858,861,863,865,867,869,871,873,875,877],{"class":86,"line":855},64,[84,857,164],{"class":90},[84,859,860],{"class":104},"img-b",[84,862,638],{"class":90},[84,864,641],{"class":104},[84,866,835],{"class":90},[84,868,838],{"class":114},[84,870,118],{"class":90},[84,872,843],{"class":482},[84,874,486],{"class":90},[84,876,849],{"class":848},[84,878,852],{"class":90},[84,880,882],{"class":86,"line":881},65,[84,883,194],{"emptyLinePlaceholder":193},[84,885,887,890,892],{"class":86,"line":886},66,[84,888,889],{"class":90},"\u003C\u002F",[84,891,95],{"class":94},[84,893,98],{"class":90},[84,895,897],{"class":86,"line":896},67,[84,898,194],{"emptyLinePlaceholder":193},[84,900,902,904,907,910,913,916,919,921],{"class":86,"line":901},68,[84,903,91],{"class":90},[84,905,906],{"class":94},"div",[84,908,909],{"class":398}," class",[84,911,912],{"class":90},"=",[84,914,915],{"class":90},"\"",[84,917,167],{"class":918},"sfyAc",[84,920,915],{"class":90},[84,922,98],{"class":90},[84,924,926,929,931,933,935,937,939,941],{"class":86,"line":925},69,[84,927,928],{"class":90},"    \u003C",[84,930,906],{"class":94},[84,932,909],{"class":398},[84,934,912],{"class":90},[84,936,915],{"class":90},[84,938,202],{"class":918},[84,940,915],{"class":90},[84,942,98],{"class":90},[84,944,946,949,951,953,955,957,960,962],{"class":86,"line":945},70,[84,947,948],{"class":90},"        \u003C",[84,950,906],{"class":94},[84,952,909],{"class":398},[84,954,912],{"class":90},[84,956,915],{"class":90},[84,958,959],{"class":918},"p-img-card img-a",[84,961,915],{"class":90},[84,963,98],{"class":90},[84,965,967,970,972,974,976,978,980,982],{"class":86,"line":966},71,[84,968,969],{"class":90},"            \u003C",[84,971,906],{"class":94},[84,973,909],{"class":398},[84,975,912],{"class":90},[84,977,915],{"class":90},[84,979,459],{"class":918},[84,981,915],{"class":90},[84,983,98],{"class":90},[84,985,987,990,992,994,996,998,1000,1002,1005,1007],{"class":86,"line":986},72,[84,988,989],{"class":90},"                \u003C",[84,991,906],{"class":94},[84,993,909],{"class":398},[84,995,912],{"class":90},[84,997,915],{"class":90},[84,999,641],{"class":918},[84,1001,915],{"class":90},[84,1003,1004],{"class":90},">\u003C\u002F",[84,1006,906],{"class":94},[84,1008,98],{"class":90},[84,1010,1012,1015,1017],{"class":86,"line":1011},73,[84,1013,1014],{"class":90},"            \u003C\u002F",[84,1016,906],{"class":94},[84,1018,98],{"class":90},[84,1020,1022,1025,1027],{"class":86,"line":1021},74,[84,1023,1024],{"class":90},"        \u003C\u002F",[84,1026,906],{"class":94},[84,1028,98],{"class":90},[84,1030,1032,1034,1036,1038,1040,1042,1045,1047],{"class":86,"line":1031},75,[84,1033,948],{"class":90},[84,1035,906],{"class":94},[84,1037,909],{"class":398},[84,1039,912],{"class":90},[84,1041,915],{"class":90},[84,1043,1044],{"class":918},"p-img-card img-b",[84,1046,915],{"class":90},[84,1048,98],{"class":90},[84,1050,1052,1054,1056,1058,1060,1062,1064,1066],{"class":86,"line":1051},76,[84,1053,969],{"class":90},[84,1055,906],{"class":94},[84,1057,909],{"class":398},[84,1059,912],{"class":90},[84,1061,915],{"class":90},[84,1063,459],{"class":918},[84,1065,915],{"class":90},[84,1067,98],{"class":90},[84,1069,1071,1073,1075,1077,1079,1081,1083,1085,1087,1089],{"class":86,"line":1070},77,[84,1072,989],{"class":90},[84,1074,906],{"class":94},[84,1076,909],{"class":398},[84,1078,912],{"class":90},[84,1080,915],{"class":90},[84,1082,641],{"class":918},[84,1084,915],{"class":90},[84,1086,1004],{"class":90},[84,1088,906],{"class":94},[84,1090,98],{"class":90},[84,1092,1094,1096,1098],{"class":86,"line":1093},78,[84,1095,1014],{"class":90},[84,1097,906],{"class":94},[84,1099,98],{"class":90},[84,1101,1103,1105,1107],{"class":86,"line":1102},79,[84,1104,1024],{"class":90},[84,1106,906],{"class":94},[84,1108,98],{"class":90},[84,1110,1112],{"class":86,"line":1111},80,[84,1113,1114],{"class":121},"　　　　　...\n",[84,1116,1118,1121,1123],{"class":86,"line":1117},81,[84,1119,1120],{"class":90},"    \u003C\u002F",[84,1122,906],{"class":94},[84,1124,98],{"class":90},[84,1126,1128,1130,1132],{"class":86,"line":1127},82,[84,1129,889],{"class":90},[84,1131,906],{"class":94},[84,1133,98],{"class":90},[95,1135,1136],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s6YsC, html code.shiki .s6YsC{--shiki-default:#B2CCD6}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":79,"searchDepth":111,"depth":111,"links":1138},[1139],{"id":27,"depth":101,"text":27},[1141],"ministack","2020-12-17","md",null,{},"\u002Farticles\u002Frecent-card-css",{"title":8,"description":8},"articles\u002Frecent-card-css",[78,1150],"css","_mix\u002Fsch-2021-04-25 22.19.26.png","a9LRbH7otDCRxRFyUY0oec3ojY88jg3a-FXDcIlgR9k",{"id":1154,"title":1155,"body":1156,"category":1290,"createdAt":1291,"description":1155,"extension":1143,"index":1144,"meta":1292,"navigation":193,"path":1293,"publish":193,"seo":1294,"series":1144,"seriesTitle":1144,"stem":1295,"tag":1296,"thumbnail":1144,"updatedAt":1144,"__hash__":1297},"articles\u002Farticles\u002Fphp-unserialize-error-reslove.md","PHPのunserializeがError at offsetエラーが起きた際の対処方法",{"type":10,"value":1157,"toc":1288},[1158,1161,1169,1176,1194,1197,1208,1215,1221,1231,1240,1243,1259,1265,1279,1282,1285],[13,1159,1160],{},"とあるデータベースの内容をCSVに出力して欲しいという簡単な仕事がありました。目的のテーブルをみてみると以下のような記録がありました。",[74,1162,1167],{"className":1163,"code":1165,"language":1166},[1164],"language-text","a:2:{i:0;s:5:“hello”;i:1;s:5:“world”;}　\u002F\u002Fサンプルです\n","text",[81,1168,1165],{"__ignoreMap":79},[13,1170,1171,1172,1175],{},"「なんじゃこれ」と調べていると、これはシリアライズされたデータでした。PHPのオブジェクトのインスタンス、配列を文字列として保存したい時に",[81,1173,1174],{},"serialize()","を使用し、その時に生成される文字でした。",[906,1177,1181,1182,1185],{"className":1178},[1179,1180],"alert","alert-success","\nserialize ( mixed $value ) : string\n",[13,1183,1184],{},"値の保存可能な表現を生成します。 型や構造を失わずに PHP の値を保存または渡す際に有用です。 シリアル化された文字列を PHP の値に戻すには、 unserialize() を使用してください。",[13,1186,1187],{},[1188,1189,1190],"small",{},[17,1191,1192],{"href":1192,"rel":1193},"https:\u002F\u002Fwww.php.net\u002Fmanual\u002Fja\u002Ffunction.serialize.php",[21],[13,1195,1196],{},"元に戻す際はunserialize()を用いると元の配列やオブジェクトになります。上の例は",[74,1198,1202],{"className":1199,"code":1200,"language":1201,"meta":79,"style":79},"language-php shiki shiki-themes material-theme-ocean","array(‘hello’, ‘world’)\n","php",[81,1203,1204],{"__ignoreMap":79},[84,1205,1206],{"class":86,"line":87},[84,1207,1200],{},[13,1209,1210,1211,1214],{},"となります。今回の記事ではそんな",[81,1212,1213],{},"unserialize()","の時に起きたエラーです。その時に以下のようなエラーが発生しました。",[74,1216,1219],{"className":1217,"code":1218,"language":1166},[1164],"Fatal error: Uncaught ErrorException: unserialize(): Error at offset XX of XX bytes\n",[81,1220,1218],{"__ignoreMap":79},[13,1222,1223,1224,1226,1227,1230],{},"このエラーの概要としては",[81,1225,1213],{},"する際に",[81,1228,1229],{},"s:5:“world”","などの部分を頼りに値を元に戻すのですが、この部分が文字コードや色々な都合で文字列数が合わない時があります。そうすると上記のようなエラーがおきます。",[13,1232,1233,1237],{},[1234,1235,1236],"strong",{},"「unserialize(): error at offset」",[1234,1238,1239],{},"「unserialize(): エラー」",[13,1241,1242],{},"と調べました。似たような症状で悩んでいる人が多いためか、すぐに解決策が出ました。最終的には以下の記事が最善の解決策でした。",[32,1244,1245,1252],{},[35,1246,1247],{},[17,1248,1251],{"href":1249,"rel":1250},"https:\u002F\u002Fkeruuweb.com\u002Fphp-unserialize-error\u002F",[21],"PHP: unserialize実行時にエラーが発生する場合の対処例",[35,1253,1254],{},[17,1255,1258],{"href":1256,"rel":1257},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F10152904\u002Fhow-to-repair-a-serialized-string-which-has-been-corrupted-by-an-incorrect-byte",[21],"How to repair a serialized string which has been corrupted by an incorrect byte count length?",[74,1260,1263],{"className":1261,"code":1262,"language":1166},[1164],"$data = 's:4:\"abc\"';\n\n$data = preg_replace_callback('!s:(\\d+):\"([\\s\\S]*?)\";!', function($m) {\n  return 's:' . strlen($m[2]) . ':\"' . $m[2] . '\";';\n}, $data);\n\necho $data; \u002F\u002F => 's:3:\"abc\"'\n\n$text = unserialize($data);\n",[81,1264,1262],{"__ignoreMap":79},[13,1266,1267,1268,1271,1272,1274,1275,1278],{},"上記のコードのように",[81,1269,1270],{},"preg_replace_callback","を用いて",[81,1273,1213],{},"できないデータにある、",[81,1276,1277],{},"'s:4:\"abc\"'","などの文字列数などを正規表現を用いて再計算し、正しい文字列数にすることでエラーが解決できます。",[13,1280,1281],{},"そもそもなぜserializeしたデータの文字列数が間違っているのかなど根本的な原因としては、PHPのバージョンの違いや変換する文字コード、保存するDBでの文字コードなど様々な要因があります。私もローカルのPHPは7.4ですが、データを保存している環境はPHP5やmysql6という古い環境で全く異なっていただったりと、エラーになる要素は満載でした。",[13,1283,1284],{},"とにかく原因とその解決方法である正規表現は正直初見殺しなので、解説記事は非常に助かりました。ありがとうございます。",[95,1286,1287],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":79,"searchDepth":111,"depth":111,"links":1289},[],[1141],"2020-12-06",{},"\u002Farticles\u002Fphp-unserialize-error-reslove",{"title":1155,"description":1155},"articles\u002Fphp-unserialize-error-reslove",[1201],"xT9EEUnxEQAaXvVrykex81iT_-hbhrt3RUP2eorH-IM",{"id":1299,"title":1300,"body":1301,"category":1380,"createdAt":1381,"description":1300,"extension":1143,"index":1144,"meta":1382,"navigation":193,"path":1383,"publish":193,"seo":1384,"series":1144,"seriesTitle":1144,"stem":1385,"tag":1386,"thumbnail":1387,"updatedAt":1144,"__hash__":1388},"articles\u002Farticles\u002Fcomposer-require-memory-leak.md","composer require で PHPがメモリ不足になるときの対処法",{"type":10,"value":1302,"toc":1378},[1303,1306,1309,1312,1320,1327,1334,1344,1347,1353,1356,1362,1369,1375],[13,1304,1305],{},"PHPのプロジェクトの時はcomposerを用います。しかしなぜかPHPのメモリエラーで必要なパッケージが全てはいらないという事象に出会いました。困ったと思い以下のワードで調べました。",[13,1307,1308],{},"「composer require メモリ不足」",[13,1310,1311],{},"そのまんまですね笑。とりあえず以下の記事が参考になりました。",[906,1313,1315],{"className":1314},[1179,1180],[17,1316,1319],{"href":1317,"target":1318},"https:\u002F\u002Fqiita.com\u002Fsetsunachan\u002Fitems\u002F48d56782d4166cef507a","_blank","【Laravel】composer require時のメモリ不足エラーの対処法【Docker】\n",[906,1321,1323],{"className":1322},[1179,1180],[17,1324,1326],{"href":1325,"target":1318},"https:\u002F\u002Fuiuifree.com\u002Fblog\u002Fdevelop\u002Fphp-composer-install-memory-limit\u002F","composer installでメモリ不足を解消するコマンド",[13,1328,1329,1330,1333],{},"方法は簡単。phpコマンドで ",[81,1331,1332],{},"php -d memory_limit=-1"," のオプションを指定してメモリ使用を無制限にします。そして",[13,1335,1336,1339,1340,1343],{},[81,1337,1338],{},"php -d memory_limit=-1 \u002Fusr\u002Fbin\u002Fcomposer require {Library_name} ","とします。phpコマンドで composerコマンドを使用せず直接 ",[81,1341,1342],{},"\u002Fusr\u002Fbin\u002Fcomposer"," とcomposerのパスを指定します。",[13,1345,1346],{},"もしも",[74,1348,1351],{"className":1349,"code":1350,"language":1166},[1164],"Could not open input file: \u002Fusr\u002Fbin\u002Fcomposer\n",[81,1352,1350],{"__ignoreMap":79},[13,1354,1355],{},"と指定したパスにcomposerがいない時、忘れてしまった時は",[74,1357,1360],{"className":1358,"code":1359,"language":1166},[1164],"$ which composer\n\u002Fusr\u002Flocal\u002Fbin\u002Fcomposer\n",[81,1361,1359],{"__ignoreMap":79},[13,1363,1364,1365,1368],{},"which コマンドでパスを明らかにしましょう。私の場合は ",[81,1366,1367],{},"\u002Fusr\u002Flocal\u002Fbin\u002Fcomposer"," にいました。なので上記のコードは",[74,1370,1373],{"className":1371,"code":1372,"language":1166},[1164],"php -d memory_limit=-1 \u002Fusr\u002Flocal\u002Fbin\u002Fcomposer require {Library_name}\n",[81,1374,1372],{"__ignoreMap":79},[13,1376,1377],{},"となります。これで無事にインストールできました。",{"title":79,"searchDepth":111,"depth":111,"links":1379},[],[1141],"2020-12-05",{},"\u002Farticles\u002Fcomposer-require-memory-leak",{"title":1300,"description":1300},"articles\u002Fcomposer-require-memory-leak",[1201],"_mix\u002Flogo-composer-transparent.png","Aa4jAWQKQAfZs5LUIbWdJrVBOI7x9mPFFgm1NNr4OAg",{"id":1390,"title":1391,"body":1392,"category":1475,"createdAt":1476,"description":1391,"extension":1143,"index":1144,"meta":1477,"navigation":193,"path":1478,"publish":193,"seo":1479,"series":1144,"seriesTitle":1144,"stem":1480,"tag":1481,"thumbnail":1144,"updatedAt":1144,"__hash__":1484},"articles\u002Farticles\u002Fdjango-nuxt-routing.md","djangoでNuxt SPAを使うときのルーティング 設定",{"type":10,"value":1393,"toc":1473},[1394,1397,1400,1405,1412,1449,1456,1471],[13,1395,1396],{},"大体のwebフレームワークではルーティングの設定ができます。最近のフロントエンド はNuxt、Nextとかjsを用いて構築して、バックエンドへの通信はAPIを用いていると思います。Djangoにもurls.pyというルーティング を定義するファイルがあります。",[13,1398,1399],{},"しかし一部のurlではNuxt.jsのjsとhtmlを返すことができず、404になってしまいます。api、adminのルートを生かしながらそのほかのURLに対してはNuxtのファイルを返すにはどうすればいいのか？それを調べていたら、以下のstack overflow の記事が答えてくれました",[13,1401,1402],{},[1234,1403,1404],{},"検索ワード「Django SPA url」",[906,1406,1408],{"className":1407},[1179,1180],[17,1409,1411],{"href":1410,"target":1318},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F42864641\u002Fhandling-single-page-application-url-and-django-url","Handling single page application url and django url",[74,1413,1417],{"className":1414,"code":1415,"language":1416,"meta":79,"style":79},"language-python shiki shiki-themes material-theme-ocean","urlpatterns = [\n    url(r'^admin\u002F', admin.site.urls),\n    url(r'^api-auth\u002F', include('rest_framework.urls', namespace='rest_framework')),\n    url(r'^api\u002F', include(router.urls)),\n    url(r'^.*$', TemplateView.as_view(template_name=\"index.html\")),\n]\n","python",[81,1418,1419,1424,1429,1434,1439,1444],{"__ignoreMap":79},[84,1420,1421],{"class":86,"line":87},[84,1422,1423],{},"urlpatterns = [\n",[84,1425,1426],{"class":86,"line":101},[84,1427,1428],{},"    url(r'^admin\u002F', admin.site.urls),\n",[84,1430,1431],{"class":86,"line":111},[84,1432,1433],{},"    url(r'^api-auth\u002F', include('rest_framework.urls', namespace='rest_framework')),\n",[84,1435,1436],{"class":86,"line":128},[84,1437,1438],{},"    url(r'^api\u002F', include(router.urls)),\n",[84,1440,1441],{"class":86,"line":134},[84,1442,1443],{},"    url(r'^.*$', TemplateView.as_view(template_name=\"index.html\")),\n",[84,1445,1446],{"class":86,"line":142},[84,1447,1448],{},"]\n",[13,1450,1451,1452,1455],{},"admin、api-auth、apiなど必要なルーティングはurlpatternsの最初の方に定義して、残りの全てのurlパターンを",[81,1453,1454],{},"r'^.*$"," という正規表現でカバーしています。ちなみにこの正規表現は「いかなる文字の全て」という意味です。そしてSPAのファイルをtemplateから引っ張ってきています。",[13,1457,1458,1459,1462,1463,1466,1467,1470],{},"こうすることで、例えば ",[81,1460,1461],{},"https:\u002F\u002F~~~\u002Faaa","、",[81,1464,1465],{},"https:\u002F\u002F~~~\u002Fbbb\u002Faa","、などはSPAのファイルが返され、あとはSPAがそのURLに対しての処理を行ってくれます。一方で",[81,1468,1469],{},"https:\u002F\u002F~~~\u002Fadmin"," とすればDjangoの場合は管理画面にアクセスできます。",[95,1472,1287],{},{"title":79,"searchDepth":111,"depth":111,"links":1474},[],[1141],"2020-12-04",{},"\u002Farticles\u002Fdjango-nuxt-routing",{"title":1391,"description":1391},"articles\u002Fdjango-nuxt-routing",[1482,1483],"django","nuxt","u-7H0bMOrS2JKrDbSIBUgp_ZgGLGpmCAjFGtTaDoVqc",{"id":1486,"title":1487,"body":1488,"category":2043,"createdAt":2045,"description":1487,"extension":1143,"index":1144,"meta":2046,"navigation":193,"path":2047,"publish":193,"seo":2048,"series":1144,"seriesTitle":1144,"stem":2049,"tag":2050,"thumbnail":1144,"updatedAt":1144,"__hash__":2052},"articles\u002Farticles\u002Fstuck-on-docker-centos8.md","Docker でcentos8+Apache2.4 の環境を作ろうとして詰まった",{"type":10,"value":1489,"toc":2039},[1490,1493,1501,1504,1507,1515,1519,1522,1625,1628,1655,1658,1685,1692,1800,1807,1811,1818,1825,1828,1983,1986,2017,2027,2036],[13,1491,1492],{},"この記事はosからイメージを指定して作ろうとして、FROM centos から構築して",[13,1494,1495,1498],{},[81,1496,1497],{},"Failed to connect to bus: No such file or directory docker",[81,1499,1500],{},"System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down",[13,1502,1503],{},"というエラーで詰まってブチ切れそうになっている人は以下の方法を確認してみてください。原因は「公式に解決法がありました」から述べていきます。",[13,1505,1506],{},"この記事で使用したOSとdockerのバージョン",[32,1508,1509,1512],{},[35,1510,1511],{},"docker：19.03.13",[35,1513,1514],{},"maxOS Catalina 10.15.5",[25,1516,1518],{"id":1517},"dockerfileからcentos8apache24の環境作ろうとした","Dockerfileからcentos8+apache2.4の環境作ろうとした",[13,1520,1521],{},"apacheのイメージを入れるとosがUbuntuだったりで、できたらosから指定したいなーと思ってcenots8の環境にapacheを入れて行こうと思い以下のようにDockerfileを記述",[74,1523,1528],{"className":1524,"code":1525,"filename":1526,"language":1527,"meta":79,"style":79},"language-dockerfile shiki shiki-themes material-theme-ocean","FROM centos:8\n\nRUN \u002Fbin\u002Fcp \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FTokyo \u002Fetc\u002Flocaltime\n\nRUN yum install -y epel-release && yum clean all\n\nRUN rpm -ivh http:\u002F\u002Fftp.riken.jp\u002FLinux\u002Fremi\u002Fenterprise\u002Fremi-release-8.rpm\n\nRUN yum -y update && yum clean all\n\nRUN yum -y install httpd && yum clean all\n\nRUN yum -y install php74-php php74-php-mysqli php74-php-gd php74-php-mbstring php74-php-opcache php74-php-xml php74-php-pear php74-php-devel php74-php-pecl-imagick php74-php-pecl-imagick-devel php74-php-pecl-zip\n\nRUN ln \u002Fusr\u002Fbin\u002Fphp74 \u002Fusr\u002Fbin\u002Fphp\n\nRUN chown -R apache:apache \u002Fvar\u002Fwww\u002Fhtml\n\nRUN systemctl enable php74-php-fpm\n\nRUN systemctl enable httpd \n","Dockerfile","dockerfile",[81,1529,1530,1535,1539,1544,1548,1553,1557,1562,1566,1571,1575,1580,1584,1589,1593,1598,1602,1607,1611,1616,1620],{"__ignoreMap":79},[84,1531,1532],{"class":86,"line":87},[84,1533,1534],{},"FROM centos:8\n",[84,1536,1537],{"class":86,"line":101},[84,1538,194],{"emptyLinePlaceholder":193},[84,1540,1541],{"class":86,"line":111},[84,1542,1543],{},"RUN \u002Fbin\u002Fcp \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FTokyo \u002Fetc\u002Flocaltime\n",[84,1545,1546],{"class":86,"line":128},[84,1547,194],{"emptyLinePlaceholder":193},[84,1549,1550],{"class":86,"line":134},[84,1551,1552],{},"RUN yum install -y epel-release && yum clean all\n",[84,1554,1555],{"class":86,"line":142},[84,1556,194],{"emptyLinePlaceholder":193},[84,1558,1559],{"class":86,"line":156},[84,1560,1561],{},"RUN rpm -ivh http:\u002F\u002Fftp.riken.jp\u002FLinux\u002Fremi\u002Fenterprise\u002Fremi-release-8.rpm\n",[84,1563,1564],{"class":86,"line":161},[84,1565,194],{"emptyLinePlaceholder":193},[84,1567,1568],{"class":86,"line":172},[84,1569,1570],{},"RUN yum -y update && yum clean all\n",[84,1572,1573],{"class":86,"line":185},[84,1574,194],{"emptyLinePlaceholder":193},[84,1576,1577],{"class":86,"line":190},[84,1578,1579],{},"RUN yum -y install httpd && yum clean all\n",[84,1581,1582],{"class":86,"line":197},[84,1583,194],{"emptyLinePlaceholder":193},[84,1585,1586],{"class":86,"line":207},[84,1587,1588],{},"RUN yum -y install php74-php php74-php-mysqli php74-php-gd php74-php-mbstring php74-php-opcache php74-php-xml php74-php-pear php74-php-devel php74-php-pecl-imagick php74-php-pecl-imagick-devel php74-php-pecl-zip\n",[84,1590,1591],{"class":86,"line":220},[84,1592,194],{"emptyLinePlaceholder":193},[84,1594,1595],{"class":86,"line":233},[84,1596,1597],{},"RUN ln \u002Fusr\u002Fbin\u002Fphp74 \u002Fusr\u002Fbin\u002Fphp\n",[84,1599,1600],{"class":86,"line":254},[84,1601,194],{"emptyLinePlaceholder":193},[84,1603,1604],{"class":86,"line":259},[84,1605,1606],{},"RUN chown -R apache:apache \u002Fvar\u002Fwww\u002Fhtml\n",[84,1608,1609],{"class":86,"line":264},[84,1610,194],{"emptyLinePlaceholder":193},[84,1612,1613],{"class":86,"line":274},[84,1614,1615],{},"RUN systemctl enable php74-php-fpm\n",[84,1617,1618],{"class":86,"line":286},[84,1619,194],{"emptyLinePlaceholder":193},[84,1621,1622],{"class":86,"line":297},[84,1623,1624],{},"RUN systemctl enable httpd\n",[13,1626,1627],{},"php入っていますがとりあえず上のような感じで必要なものを入れて、apacheを常にONにすれば行けるだろ思い、ビルドする",[74,1629,1633],{"className":1630,"code":1631,"language":1632,"meta":79,"style":79},"language-bash shiki shiki-themes material-theme-ocean","$ docker build -t centos_apche:1.0 .\n","bash",[81,1634,1635],{"__ignoreMap":79},[84,1636,1637,1640,1643,1646,1649,1652],{"class":86,"line":87},[84,1638,1639],{"class":104},"$",[84,1641,1642],{"class":918}," docker",[84,1644,1645],{"class":918}," build",[84,1647,1648],{"class":918}," -t",[84,1650,1651],{"class":918}," centos_apche:1.0",[84,1653,1654],{"class":918}," .\n",[13,1656,1657],{},"ビルドは普通に成功しました。イメージが作られたのを確認していざコンテナを起動。",[74,1659,1661],{"className":1630,"code":1660,"language":1632,"meta":79,"style":79},"docker run -t -d -p 9000:80 centos_apche:1.0\n",[81,1662,1663],{"__ignoreMap":79},[84,1664,1665,1668,1671,1673,1676,1679,1682],{"class":86,"line":87},[84,1666,1667],{"class":104},"docker",[84,1669,1670],{"class":918}," run",[84,1672,1648],{"class":918},[84,1674,1675],{"class":918}," -d",[84,1677,1678],{"class":918}," -p",[84,1680,1681],{"class":918}," 9000:80",[84,1683,1684],{"class":918}," centos_apche:1.0\n",[13,1686,1687,1688,1691],{},"しかし localhost:9000 にアクセスすると「データが送信されませんでした」と真っ黒（chrome）。",[81,1689,1690],{},"docker ps"," で稼働状態を調べてもコンテナはきちんと動いている。つまりコンテナの中で何か起きている。",[74,1693,1695],{"className":1630,"code":1694,"language":1632,"meta":79,"style":79},"docker exec -it {container_id or name} \u002Fbin\u002Fbash\n...\n[root@32212888398b \u002F]# \n[root@32212888398b \u002F]# systemctl httpd status \nSystem has not been booted with systemd as init system (PID 1). Can't operate. \nFailed to connect to bus: Host is down\n",[81,1696,1697,1719,1724,1738,1749,1795],{"__ignoreMap":79},[84,1698,1699,1701,1704,1707,1710,1713,1716],{"class":86,"line":87},[84,1700,1667],{"class":104},[84,1702,1703],{"class":918}," exec",[84,1705,1706],{"class":918}," -it",[84,1708,1709],{"class":918}," {container_id",[84,1711,1712],{"class":918}," or",[84,1714,1715],{"class":918}," name}",[84,1717,1718],{"class":918}," \u002Fbin\u002Fbash\n",[84,1720,1721],{"class":86,"line":101},[84,1722,1723],{"class":482},"...\n",[84,1725,1726,1729,1732,1735],{"class":86,"line":111},[84,1727,1728],{"class":90},"[",[84,1730,1731],{"class":121},"root@32212888398b \u002F",[84,1733,1734],{"class":90},"]",[84,1736,1737],{"class":121},"# \n",[84,1739,1740,1742,1744,1746],{"class":86,"line":128},[84,1741,1728],{"class":90},[84,1743,1731],{"class":121},[84,1745,1734],{"class":90},[84,1747,1748],{"class":121},"# systemctl httpd status \n",[84,1750,1751,1754,1757,1760,1763,1766,1769,1772,1775,1778,1781,1784,1786,1789,1792],{"class":86,"line":134},[84,1752,1753],{"class":104},"System",[84,1755,1756],{"class":918}," has",[84,1758,1759],{"class":918}," not",[84,1761,1762],{"class":918}," been",[84,1764,1765],{"class":918}," booted",[84,1767,1768],{"class":918}," with",[84,1770,1771],{"class":918}," systemd",[84,1773,1774],{"class":918}," as",[84,1776,1777],{"class":918}," init",[84,1779,1780],{"class":918}," system",[84,1782,1783],{"class":121}," (PID ",[84,1785,720],{"class":150},[84,1787,1788],{"class":121},"). Can",[84,1790,1791],{"class":90},"'",[84,1793,1794],{"class":918},"t operate. \n",[84,1796,1797],{"class":86,"line":142},[84,1798,1799],{"class":918},"Failed to connect to bus: Host is down\n",[13,1801,1802,1803,1806],{},"「は？」とりあえずhttpdが動いていないそうな上に「Host is down」とos自体がダメと言うことか？",[81,1804,1805],{},"systemctl","だけ打ってもエラーしか出ない。",[25,1808,1810],{"id":1809},"公式に解決法がありました","公式に解決法がありました。",[13,1812,1813,1814,1817],{},"いろいろ調べましたが ",[81,1815,1816],{},"systemd"," を起動する方法がcentos7、centos:latest（つまり８）ではデフォルトでアクティブにならず一工夫必要だそうです。",[13,1819,1820],{},[17,1821,1824],{"href":1822,"rel":1823},"https:\u002F\u002Fhub.docker.com\u002F_\u002Fcentos",[21],"公式ドキュメント",[13,1826,1827],{},"私も詳しい仕組みまではわからないのですが、まずDockerfileを以下のように追記します。",[74,1829,1831],{"className":1524,"code":1830,"filename":1526,"language":1527,"meta":79,"style":79},"FROM centos:8\n#ここから\nENV container docker\nRUN (cd \u002Flib\u002Fsystemd\u002Fsystem\u002Fsysinit.target.wants\u002F; for i in *; do [ $i == \\\nsystemd-tmpfiles-setup.service ] || rm -f $i; done); \\\nrm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fmulti-user.target.wants\u002F*;\\\nrm -f \u002Fetc\u002Fsystemd\u002Fsystem\u002F*.wants\u002F*;\\\nrm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Flocal-fs.target.wants\u002F*; \\\nrm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fsockets.target.wants\u002F*udev*; \\\nrm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fsockets.target.wants\u002F*initctl*; \\\nrm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fbasic.target.wants\u002F*;\\\nrm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fanaconda.target.wants\u002F*;\nVOLUME [ \"\u002Fsys\u002Ffs\u002Fcgroup\" ]\nCMD [\"\u002Fusr\u002Fsbin\u002Finit\"]\n#ここまで\nRUN \u002Fbin\u002Fcp \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FTokyo \u002Fetc\u002Flocaltime\n\nRUN yum install -y epel-release && yum clean all\n\nRUN rpm -ivh http:\u002F\u002Fftp.riken.jp\u002FLinux\u002Fremi\u002Fenterprise\u002Fremi-release-8.rpm\n\nRUN yum -y update && yum clean all\n\nRUN yum -y install httpd && yum clean all\n\nRUN yum -y install php74-php php74-php-mysqli php74-php-gd php74-php-mbstring php74-php-opcache php74-php-xml php74-php-pear php74-php-devel php74-php-pecl-imagick php74-php-pecl-imagick-devel php74-php-pecl-zip\n\nRUN ln \u002Fusr\u002Fbin\u002Fphp74 \u002Fusr\u002Fbin\u002Fphp\n\nRUN chown -R apache:apache \u002Fvar\u002Fwww\u002Fhtml\n\nRUN systemctl enable php74-php-fpm\n\nRUN systemctl enable httpd \n",[81,1832,1833,1837,1842,1847,1852,1857,1862,1867,1872,1877,1882,1887,1892,1897,1902,1907,1911,1915,1919,1923,1927,1931,1935,1939,1943,1947,1951,1955,1959,1963,1967,1971,1975,1979],{"__ignoreMap":79},[84,1834,1835],{"class":86,"line":87},[84,1836,1534],{},[84,1838,1839],{"class":86,"line":101},[84,1840,1841],{},"#ここから\n",[84,1843,1844],{"class":86,"line":111},[84,1845,1846],{},"ENV container docker\n",[84,1848,1849],{"class":86,"line":128},[84,1850,1851],{},"RUN (cd \u002Flib\u002Fsystemd\u002Fsystem\u002Fsysinit.target.wants\u002F; for i in *; do [ $i == \\\n",[84,1853,1854],{"class":86,"line":134},[84,1855,1856],{},"systemd-tmpfiles-setup.service ] || rm -f $i; done); \\\n",[84,1858,1859],{"class":86,"line":142},[84,1860,1861],{},"rm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fmulti-user.target.wants\u002F*;\\\n",[84,1863,1864],{"class":86,"line":156},[84,1865,1866],{},"rm -f \u002Fetc\u002Fsystemd\u002Fsystem\u002F*.wants\u002F*;\\\n",[84,1868,1869],{"class":86,"line":161},[84,1870,1871],{},"rm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Flocal-fs.target.wants\u002F*; \\\n",[84,1873,1874],{"class":86,"line":172},[84,1875,1876],{},"rm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fsockets.target.wants\u002F*udev*; \\\n",[84,1878,1879],{"class":86,"line":185},[84,1880,1881],{},"rm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fsockets.target.wants\u002F*initctl*; \\\n",[84,1883,1884],{"class":86,"line":190},[84,1885,1886],{},"rm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fbasic.target.wants\u002F*;\\\n",[84,1888,1889],{"class":86,"line":197},[84,1890,1891],{},"rm -f \u002Flib\u002Fsystemd\u002Fsystem\u002Fanaconda.target.wants\u002F*;\n",[84,1893,1894],{"class":86,"line":207},[84,1895,1896],{},"VOLUME [ \"\u002Fsys\u002Ffs\u002Fcgroup\" ]\n",[84,1898,1899],{"class":86,"line":220},[84,1900,1901],{},"CMD [\"\u002Fusr\u002Fsbin\u002Finit\"]\n",[84,1903,1904],{"class":86,"line":233},[84,1905,1906],{},"#ここまで\n",[84,1908,1909],{"class":86,"line":254},[84,1910,1543],{},[84,1912,1913],{"class":86,"line":259},[84,1914,194],{"emptyLinePlaceholder":193},[84,1916,1917],{"class":86,"line":264},[84,1918,1552],{},[84,1920,1921],{"class":86,"line":274},[84,1922,194],{"emptyLinePlaceholder":193},[84,1924,1925],{"class":86,"line":286},[84,1926,1561],{},[84,1928,1929],{"class":86,"line":297},[84,1930,194],{"emptyLinePlaceholder":193},[84,1932,1933],{"class":86,"line":302},[84,1934,1570],{},[84,1936,1937],{"class":86,"line":318},[84,1938,194],{"emptyLinePlaceholder":193},[84,1940,1941],{"class":86,"line":334},[84,1942,1579],{},[84,1944,1945],{"class":86,"line":339},[84,1946,194],{"emptyLinePlaceholder":193},[84,1948,1949],{"class":86,"line":352},[84,1950,1588],{},[84,1952,1953],{"class":86,"line":365},[84,1954,194],{"emptyLinePlaceholder":193},[84,1956,1957],{"class":86,"line":378},[84,1958,1597],{},[84,1960,1961],{"class":86,"line":383},[84,1962,194],{"emptyLinePlaceholder":193},[84,1964,1965],{"class":86,"line":388},[84,1966,1606],{},[84,1968,1969],{"class":86,"line":404},[84,1970,194],{"emptyLinePlaceholder":193},[84,1972,1973],{"class":86,"line":417},[84,1974,1615],{},[84,1976,1977],{"class":86,"line":429},[84,1978,194],{"emptyLinePlaceholder":193},[84,1980,1981],{"class":86,"line":444},[84,1982,1624],{},[13,1984,1985],{},"そしてビルドは普通に行い、コンテナを立ち上げる際はボリュームともう一つオプションを指定します。",[74,1987,1989],{"className":1630,"code":1988,"language":1632,"meta":79,"style":79},"docker run -v \u002Fsys\u002Ffs\u002Fcgroup:\u002Fsys\u002Ffs\u002Fcgroup:ro --privileged -t -d -p 9000:80 centos_apched:1.0\n",[81,1990,1991],{"__ignoreMap":79},[84,1992,1993,1995,1997,2000,2003,2006,2008,2010,2012,2014],{"class":86,"line":87},[84,1994,1667],{"class":104},[84,1996,1670],{"class":918},[84,1998,1999],{"class":918}," -v",[84,2001,2002],{"class":918}," \u002Fsys\u002Ffs\u002Fcgroup:\u002Fsys\u002Ffs\u002Fcgroup:ro",[84,2004,2005],{"class":918}," --privileged",[84,2007,1648],{"class":918},[84,2009,1675],{"class":918},[84,2011,1678],{"class":918},[84,2013,1681],{"class":918},[84,2015,2016],{"class":918}," centos_apched:1.0\n",[13,2018,2019,2022,2023,2026],{},[81,2020,2021],{},"-v \u002Fsys\u002Ffs\u002Fcgroup:\u002Fsys\u002Ffs\u002Fcgroup:ro","でファイルシステムをホストマシンからボリュームし、",[81,2024,2025],{},"--privileged","でコンテナにホストマシンのroot権限を与ます。どちらか片方が抜けてもいけません。",[13,2028,2029,2030,2032,2033,2035],{},"このオプションとボリュームを指定すると",[81,2031,1816],{},"が動きます。実際にコンテナに入っていくと",[81,2034,1805],{},"コマンドが実行されました。",[95,2037,2038],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":79,"searchDepth":111,"depth":111,"links":2040},[2041,2042],{"id":1517,"depth":101,"text":1518},{"id":1809,"depth":101,"text":1810},[2044],"devstack","2020-11-27",{},"\u002Farticles\u002Fstuck-on-docker-centos8",{"title":1487,"description":1487},"articles\u002Fstuck-on-docker-centos8",[2051,1667],"infrastructure","yvD5JRk2N7X9ToHbPIm-hQQm0dyCSLPsVEnjEF9H-RE",{"id":2054,"title":2055,"body":2056,"category":2485,"createdAt":2486,"description":2055,"extension":1143,"index":1144,"meta":2487,"navigation":193,"path":2488,"publish":193,"seo":2489,"series":1144,"seriesTitle":1144,"stem":2490,"tag":2491,"thumbnail":2493,"updatedAt":1144,"__hash__":2494},"articles\u002Farticles\u002Fwordpress-cli-with-cron.md","wordpress CLIとcronを用いてプラグインの関数を定期自動実行させる",{"type":10,"value":2057,"toc":2475},[2058,2066,2073,2076,2083,2087,2095,2100,2106,2109,2115,2122,2126,2133,2139,2146,2152,2155,2160,2163,2169,2172,2175,2178,2181,2185,2195,2208,2289,2292,2298,2301,2373,2376,2379,2382,2385,2391,2397,2408,2414,2428,2431,2446,2449,2457,2464,2470,2473],[13,2059,2060,2061,2065],{},"こんにちはjunです。",[17,2062,2064],{"href":2063},"\u002Farticles\u002Fmuch-post-migration-wp","「別CMSで作成された4万件分の大量投稿をwordpressに引越しする」","の記事であったように４万件のデータをマルチサイト構成で移行したことがありました。そこで新しい課題として、親サイトでは登録した他のサイト全ての投稿をページングで一覧で表示するという仕様にぶち当たりました。",[13,2067,2068,2069,2072],{},"基本的にwordpressは",[81,2070,2071],{},"get_posts()","を使えば簡単にページングも兼ね備えた一覧ページを作れるのですが、マルチサイト構成の場合は上手くいかずDBから上手く引っ張る必要がありました。しかしマルチサイトだけでも75サイトあり、そこから４万件分の投稿をとる必要があります。表示のたびにそんな高コストの処理はできないので、予め必要なデータをまとめたキャッシュテーブルを作ることで解決しました。",[13,2074,2075],{},"ただしキャッシュテーブルの弱点は更新が必要なことです。手動の更新は実装できましたが、1時間に1回自動で実行するなどの実装が必要となります。すぐに思いついた方法としてはcronを用いてキャッシュ作成の関数を叩くことです。",[13,2077,2078,2079,2082],{},"wordpressにcronやCLIでできないかな〜？と調べていたら公式で",[1234,2080,2081],{},"wp CLI","というものが使えると聞き、見様見真似で実装ができました。wordpressで重い処理の定期実行などを考えている方はぜひ参考にしてみてください。",[25,2084,2086],{"id":2085},"wp-cli-をインストール","wp cli をインストール",[13,2088,2089,2094],{},[17,2090,2093],{"href":2091,"rel":2092},"https:\u002F\u002Fmake.wordpress.org\u002Fcli\u002Fhandbook\u002Fguides\u002Finstalling\u002F",[21],"公式サイト","にあるようにcliを使用するにはコマンドラインを通じてインストールする必要があります。",[2096,2097,2099],"h3",{"id":2098},"curlなどで-pharファイルを読み込む","curlなどで pharファイルを読み込む",[74,2101,2104],{"className":2102,"code":2103,"language":1166},[1164],"curl -O https:\u002F\u002Fraw.githubusercontent.com\u002Fwp-cli\u002Fbuilds\u002Fgh-pages\u002Fphar\u002Fwp-cli.phar\n",[81,2105,2103],{"__ignoreMap":79},[13,2107,2108],{},"ドキュメントルート あたりで行ってみましょう。wp-cli.pharというファイルが出現します。これだけでも",[74,2110,2113],{"className":2111,"code":2112,"language":1166},[1164],"php wp-cli.phar --info\n",[81,2114,2112],{"__ignoreMap":79},[13,2116,2117,2118,2121],{},"という風にして実行することができます。",[81,2119,2120],{},"wp ~~"," と実行するためにはもう一工夫必要です。",[2096,2123,2125],{"id":2124},"実行権限をあたえ移動","実行権限をあたえ、移動",[13,2127,2128,2129,2132],{},"以下のようにして実行権限を付与し、",[81,2130,2131],{},"wp","というコマンドで打てるようにします。",[74,2134,2137],{"className":2135,"code":2136,"language":1166},[1164],"chmod +x wp-cli.phar\nsudo mv wp-cli.phar \u002Fusr\u002Flocal\u002Fbin\u002Fwp\n",[81,2138,2136],{"__ignoreMap":79},[13,2140,2141,2142,2145],{},"はい。これでOKです。",[81,2143,2144],{},"wp --info","を打ってみると",[74,2147,2150],{"className":2148,"code":2149,"language":1166},[1164],"$ wp --info\nOS:     Linux 4.19.76-linuxkit #1 SMP Tue May 26 11:42:35 UTC 2020 x86_64\nShell:  \u002Fbin\u002Fsh\nPHP binary:     \u002Fusr\u002Flocal\u002Fbin\u002Fphp\nPHP version:    7.4.12\nphp.ini used:\nWP-CLI root dir:        phar:\u002F\u002Fwp-cli.phar\u002Fvendor\u002Fwp-cli\u002Fwp-cli\nWP-CLI vendor dir:      phar:\u002F\u002Fwp-cli.phar\u002Fvendor\nWP_CLI phar path:       \u002Fvar\u002Fwww\u002Fhtml\nWP-CLI packages dir:\nWP-CLI global config:\nWP-CLI project config:\nWP-CLI version: 2.4.0\n",[81,2151,2149],{"__ignoreMap":79},[13,2153,2154],{},"こんな感じに情報が出てくればきちんと機能しています。",[2156,2157,2159],"h4",{"id":2158},"tips-rootでやると怒られる","TIPS rootでやると怒られる",[13,2161,2162],{},"rootユーザーでコマンドを実行しようとすると以下のように怒られます。",[74,2164,2167],{"className":2165,"code":2166,"language":1166},[1164],"Error: YIKES! It looks like you're running this as root. You probably meant to run this as the user that your WordPress installation exists under.\n\nIf you REALLY mean to run this as root, we won't stop you, but just bear in mind that any code on this site will then have full control of your server, making it quite DANGEROUS.\n\nIf you'd like to continue as root, please run this again, adding this flag:  --allow-root\n\nIf you'd like to run it as the user that this site is under, you can run the following to become the respective user:\n\n    sudo -u USER -i -- wp \u003Ccommand>\n",[81,2168,2166],{"__ignoreMap":79},[13,2170,2171],{},"ざっくり和訳すると",[13,2173,2174],{},"「ええっ！wpコマンドをrootユーザーで実行しようとしてない！？rootユーザーはサーバーもいろいろ弄れるから危ないよ。まあ、あなたがどうしてもrootユーザーで実行したいなら止めないけど。その時は –allow-root オプションを添えてね」",[13,2176,2177],{},"こんな感じです。rootユーザーはサーバーの最高権限を持っているので使うのは危ないです。さらにwpコマンドを通じてwordpressの内容を容易に破壊することも可能なので、rootでやるのはおすすめしないと警告されます。",[13,2179,2180],{},"回避する場合は警告の通り –allow-root オプションを添えるか、ユーザーを変更して実行します。実務サーバーではアプリケーションをrootで運用することはないので特に問題ないとは思います。",[25,2182,2184],{"id":2183},"自作プラグインの関数にフックさせる","（自作）プラグインの関数にフックさせる",[13,2186,2187,2188,2190,2191,2194],{},"それではCLIがインストールされた所でプラグインの関数にフックさせてみましょう。方法は沢山ありますが簡単なのは自作のコマンドを追加してしますことです。CLIをインストールして、",[81,2189,2131],{},"コマンドを実行すると",[81,2192,2193],{},"WP_CLI","クラスが実行時に使用可能になります。",[13,2196,2197,2198,2203,2204,2207],{},"WP_CLIクラスには",[17,2199,2202],{"href":2200,"rel":2201},"https:\u002F\u002Fmake.wordpress.org\u002Fcli\u002Fhandbook\u002Freferences\u002Finternal-api\u002F",[21],"こちらのリファランス","のように静的メソッドがいくつか用意されています。その中に",[81,2205,2206],{},"WP_CLI::add_command()","をコマンドを追加できるメソッドがあります。第一引数にコマンド名、第二引数にコールバック関数名または関数を指定します。",[74,2209,2212],{"className":1199,"code":2210,"filename":2211,"language":1201,"meta":79,"style":79},"\u003C?php\n\u002F*\nPlugin Name: test\n*\u002F\n\nif(!defined('ABSPATH')) {\n    die('You are not allowed to call this page directly.');\n}\n\nfunction cmm_test(){\n    echo 'test!';\n}\n\nif ( class_exists( 'WP_CLI' ) ) {\n    WP_CLI::add_command( 'foo', 'cmm_test' );\n}\n","plugins\u002Ftest\u002Ftest.php",[81,2213,2214,2219,2224,2229,2234,2238,2243,2248,2253,2257,2262,2267,2271,2275,2280,2285],{"__ignoreMap":79},[84,2215,2216],{"class":86,"line":87},[84,2217,2218],{},"\u003C?php\n",[84,2220,2221],{"class":86,"line":101},[84,2222,2223],{},"\u002F*\n",[84,2225,2226],{"class":86,"line":111},[84,2227,2228],{},"Plugin Name: test\n",[84,2230,2231],{"class":86,"line":128},[84,2232,2233],{},"*\u002F\n",[84,2235,2236],{"class":86,"line":134},[84,2237,194],{"emptyLinePlaceholder":193},[84,2239,2240],{"class":86,"line":142},[84,2241,2242],{},"if(!defined('ABSPATH')) {\n",[84,2244,2245],{"class":86,"line":156},[84,2246,2247],{},"    die('You are not allowed to call this page directly.');\n",[84,2249,2250],{"class":86,"line":161},[84,2251,2252],{},"}\n",[84,2254,2255],{"class":86,"line":172},[84,2256,194],{"emptyLinePlaceholder":193},[84,2258,2259],{"class":86,"line":185},[84,2260,2261],{},"function cmm_test(){\n",[84,2263,2264],{"class":86,"line":190},[84,2265,2266],{},"    echo 'test!';\n",[84,2268,2269],{"class":86,"line":197},[84,2270,2252],{},[84,2272,2273],{"class":86,"line":207},[84,2274,194],{"emptyLinePlaceholder":193},[84,2276,2277],{"class":86,"line":220},[84,2278,2279],{},"if ( class_exists( 'WP_CLI' ) ) {\n",[84,2281,2282],{"class":86,"line":233},[84,2283,2284],{},"    WP_CLI::add_command( 'foo', 'cmm_test' );\n",[84,2286,2287],{"class":86,"line":254},[84,2288,2252],{},[13,2290,2291],{},"例えば上記のようにした場合は以下のように実行されます。",[74,2293,2296],{"className":2294,"code":2295,"language":1166},[1164],"$ wp foo\ntest!\n",[81,2297,2295],{"__ignoreMap":79},[13,2299,2300],{},"なので同じようにして関数を定義して、フックさせます。",[74,2302,2304],{"className":1199,"code":2303,"language":1201,"meta":79,"style":79},"\u003C?php\n\u002F*\nPlugin Name: test\n*\u002F\n\nif(!defined('ABSPATH')) {\n    die('You are not allowed to call this page directly.');\n}\n\nfunction create_cache_table(){\n    .... \u002F\u002F めちゃくちゃ時間のかかる処理など\n}\n\nif ( class_exists( 'WP_CLI' ) ) {\n    WP_CLI::add_command( 'create_cache', 'create_cache_table' );\n}\n",[81,2305,2306,2310,2314,2318,2322,2326,2330,2334,2338,2342,2347,2352,2356,2360,2364,2369],{"__ignoreMap":79},[84,2307,2308],{"class":86,"line":87},[84,2309,2218],{},[84,2311,2312],{"class":86,"line":101},[84,2313,2223],{},[84,2315,2316],{"class":86,"line":111},[84,2317,2228],{},[84,2319,2320],{"class":86,"line":128},[84,2321,2233],{},[84,2323,2324],{"class":86,"line":134},[84,2325,194],{"emptyLinePlaceholder":193},[84,2327,2328],{"class":86,"line":142},[84,2329,2242],{},[84,2331,2332],{"class":86,"line":156},[84,2333,2247],{},[84,2335,2336],{"class":86,"line":161},[84,2337,2252],{},[84,2339,2340],{"class":86,"line":172},[84,2341,194],{"emptyLinePlaceholder":193},[84,2343,2344],{"class":86,"line":185},[84,2345,2346],{},"function create_cache_table(){\n",[84,2348,2349],{"class":86,"line":190},[84,2350,2351],{},"    .... \u002F\u002F めちゃくちゃ時間のかかる処理など\n",[84,2353,2354],{"class":86,"line":197},[84,2355,2252],{},[84,2357,2358],{"class":86,"line":207},[84,2359,194],{"emptyLinePlaceholder":193},[84,2361,2362],{"class":86,"line":220},[84,2363,2279],{},[84,2365,2366],{"class":86,"line":233},[84,2367,2368],{},"    WP_CLI::add_command( 'create_cache', 'create_cache_table' );\n",[84,2370,2371],{"class":86,"line":254},[84,2372,2252],{},[13,2374,2375],{},"ちなみにif ( class_exists( 'WP_CLI' ) ) がないとUndefined type 'WP_CLI'というエラーが発生して怒られます。ドキュメントルート 配下のwordpressフォルダにはWP_CLIクラスのソースがなく、コマンドを実行（wp-cli.pharを実行）するときにクラスが提供されるそうです。一応これが標準らしい。",[13,2377,2378],{},"コマンドが追加できたのであとはcronを設定してwpコマンドを設定してあげます。",[25,2380,2381],{"id":2381},"cronを設定",[13,2383,2384],{},"rootでないユーザーで操作しているものとします。",[74,2386,2389],{"className":2387,"code":2388,"language":1166},[1164],"$ crontab -e\n",[81,2390,2388],{"__ignoreMap":79},[74,2392,2395],{"className":2393,"code":2394,"language":1166},[1164],"PATH = \u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Flocal\u002Fsbin:\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fsbin:\u002Fusr\u002Fbin:\u002Fsbin:\u002Fbin;\n0 * * * * cd \u002Fvar\u002Fwww\u002Fhtml && wp create_cache >> \u002Fdev\u002Fnull 2>&1\n",[81,2396,2394],{"__ignoreMap":79},[13,2398,2399,2400,2403,2404,2407],{},"このcronは1時間ごと（00分）に実行されます。",[81,2401,2402],{},"cd \u002Fvar\u002Fwww\u002Fhtm","l でドキュメントルート へ移動して先ほどのコマンドを打って、思い処理の関数を実行しています。ちなみに上の ",[81,2405,2406],{},"PATH = ...."," がないと",[74,2409,2412],{"className":2410,"code":2411,"language":1166},[1164],"\u002Fusr\u002Fbin\u002Fenv: 'php': No such file or directory\n",[81,2413,2411],{"__ignoreMap":79},[13,2415,2416,2417,604,2420,2423,2424,2427],{},"というようにエラーが出てしまい、指定のcron処理が行われません。これ結構ハマったのですが、cron実行時のPATHの設定が",[81,2418,2419],{},"\u002Fbin",[81,2421,2422],{},"\u002Fusr\u002Fbin","だけしか通っていないので、PHPやwpが存在する ",[81,2425,2426],{},"\u002Fusr\u002Flocal\u002Fbin","配下を見ておらずPHPが実行できないのが原因です。",[13,2429,2430],{},"なので予め普段使用しているPATHを出しておいて、crontab上で再定義しています。上記のようにすればあとは指定の時間が来れば",[2432,2433,2434,2437,2440,2443],"ol",{},[35,2435,2436],{},"cronでコマンドを実行",[35,2438,2439],{},"コマンドに結びついた関数を呼び出す",[35,2441,2442],{},"処理が実行",[35,2444,2445],{},"ログがあればそれに吐き出す",[13,2447,2448],{},"と言った自動運用が可能になります。",[906,2450,2453,2454],{"className":2451},[1179,2452],"alert-info","\n「>> \u002Fdev\u002Fnull 2>&1」 これは「エラー出力を標準出力とし、\u002Fdev\u002Fnullに書き出す」という意味です。\n",[13,2455,2456],{},"「？」となった方にもう簡単説明します。>> で実行したコマンドの結果などを出力できます。エラーであればエラー文が得られます。そして \u002Fdev\u002Fnull はunixのスペシャルファイルで、空のファイルです。空のファイルを指定して結果を捨てることができます。「無に書き出す」みたいなことをしています。2>&1 はエラー出力を標準出力にします。という意味です。",[13,2458,2459,2460,2463],{},"つまり",[81,2461,2462],{},">> \u002Fdev\u002Fnull 2>&1","はエラー出力であっても結果内容を残さず捨てるという意味です。テストぐらいであれば問題ないのですが、実務でやると何か起きたときに原因が分からないので以下のようにするといいです。",[74,2465,2468],{"className":2466,"code":2467,"language":1166},[1164],"PATH = \u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Flocal\u002Fsbin:\u002Fusr\u002Flocal\u002Fbin:\u002Fusr\u002Fsbin:\u002Fusr\u002Fbin:\u002Fsbin:\u002Fbin;\n0 * * * * cd \u002Fvar\u002Fwww\u002Fhtml && wp create_cache >> \u002Fvar\u002Flog\u002Fcron.log 2>&1\n",[81,2469,2467],{"__ignoreMap":79},[13,2471,2472],{},"こうするときちんとログファイルにエラーが残ります。",[95,2474,1287],{},{"title":79,"searchDepth":111,"depth":111,"links":2476},[2477,2483,2484],{"id":2085,"depth":101,"text":2086,"children":2478},[2479,2480],{"id":2098,"depth":111,"text":2099},{"id":2124,"depth":111,"text":2125,"children":2481},[2482],{"id":2158,"depth":128,"text":2159},{"id":2183,"depth":101,"text":2184},{"id":2381,"depth":101,"text":2381},[2044],"2020-11-26",{},"\u002Farticles\u002Fwordpress-cli-with-cron",{"title":2055,"description":2055},"articles\u002Fwordpress-cli-with-cron",[1201,2492],"wordpress","_mix\u002FWordPress-logotype-wmark-e1606319357320.png","1F5rQcoO_KWxxu1bCXWSeo1uX58qewfjTBZ8Az0AndI",{"id":2496,"title":2497,"body":2498,"category":2594,"createdAt":2595,"description":2497,"extension":1143,"index":1144,"meta":2596,"navigation":193,"path":2597,"publish":193,"seo":2598,"series":1144,"seriesTitle":1144,"stem":2599,"tag":2600,"thumbnail":1144,"updatedAt":1144,"__hash__":2602},"articles\u002Farticles\u002Fwhat-ssg-ssr-spa.md","Nuxt.jsのSPA、SSR、SSGって何？",{"type":10,"value":2499,"toc":2587},[2500,2503,2507,2521,2534,2548,2552,2555,2558,2561,2565,2568,2571,2574,2577,2581,2584],[13,2501,2502],{},"こんにちはjunです。Nuxt.jsを学びはじめにこれら３つの用語が？？？となったのを思い出し、復習がてら解説したいともいます。",[25,2504,2506],{"id":2505},"ssrssgspaって何","SSR・SSG・SPAって何？",[13,2508,2509,2510,2513,2514,2516,2517,2520],{},"SSRは ",[1234,2511,2512],{},"S","erver ",[1234,2515,2512],{},"ide ",[1234,2518,2519],{},"R","enderingのことでVueなどによるjsレンダリングをブラウザではなくサーバーで行うことです。",[13,2522,2523,2524,2526,2527,2529,2530,2533],{},"SSGは ",[1234,2525,2512],{},"tatic ",[1234,2528,2512],{},"ite ",[1234,2531,2532],{},"G","enerateのことで静的書き出しとも言われています。HTML、CSS、JSだけで構成されるファイルを生成することをいいます。",[13,2535,2536,2537,2539,2540,2543,2544,2547],{},"SPAは ",[1234,2538,2512],{},"ingle ",[1234,2541,2542],{},"P","age ",[1234,2545,2546],{},"A","pplication と言われレンダーから何もかもブラウザ側で実行します。初期表示は遅いのでローディングは必須ですが、ページ遷移を感じさせず軽快なUIは非常に魅力的です。SEOを気にせず、操作するブラウザ環境が比較的新しいものに限定するならば選択しても良いと思います。内部的なwebアプリの操作画面などに使用されることが多いです。",[2096,2549,2551],{"id":2550},"ssrの利点","SSRの利点",[13,2553,2554],{},"javaScriptは基本的にブラウザ側、つまり閲覧者のマシンで行われます。VueやReactはjsを用いてHTMLをレンダー（構築）するので、ブラウザ側でHTMLが完成します。PHPなどで書かれたwebサービスはサーバー側で完成したHTMLを閲覧者に配信します。",[13,2556,2557],{},"ブラウザでのレンダリングを前提に構築すると、サーバー側のHTMLは最低限の記述しかないためSEOが弱かったり、ブラウザ側の表示に時間がかかったり、古いブラウザや低スペックのスマホだと表示・動作が遅かったりするデメリットがあります。それを解決するのがSSRです。",[13,2559,2560],{},"本来ブラウザで実行されるjsをサーバー側で実行して、完成したHTMLを配信します。この場合、サーバーにはnode.jsが必要になりますが、クローラーは完成したHTMLを読み込み、またユーザー側も高速で表示することができます。",[2096,2562,2564],{"id":2563},"ssgの利点","SSGの利点",[13,2566,2567],{},"昨今のwebアプリ・サイトはサーバーからの情報を当て込んだり、Ajaxなどで常に通信していることが多いです。しかしそれはwebアプリ（ブラウザ側）からサーバーへアクセスできる穴が必要で、そこを狙った脆弱性を通じてサーバーが攻撃されることがあります。（webアプリのソースとサーバで動く言語、DBが同居しているので危ない）",[13,2569,2570],{},"他にもサーバー側の実行が遅いと結果的にブラウザの表示が遅くなったり、もっと高速に表示したい場合はSSG、静的書き出しを行います。静的に書き出す際にjsを実行しますし、設計によってはあらかじめサーバーからの取得した情報も読み込んでおきます。",[13,2572,2573],{},"静的に書き出したものはHTML、CSS、JSだけであり基本的にはサーバーと通信して、DBからデータを取ってきたりなどもしません。そのためPHPやDBがインストールされていないサーバーに置いて、配信することが可能です。",[13,2575,2576],{},"配信側にサーバーへ対する攻撃手段が残らないのでセキュリティも格段に上がりますし、表示速度もある程度上がります。これがSSGの利点です。",[2096,2578,2580],{"id":2579},"spaの利点","SPAの利点",[13,2582,2583],{},"SPAの利点は実装のしやすさと軽快なUIにあります。レンダーはブラウザで行われるのでサーバーには最低限のHTMLとJSが配信される様にしておけばOKです。SSG・SSRはサーバーにnode.jsを入れたり、webサーバーとあれこりしたりと配信する前に手間がかかります。",[13,2585,2586],{},"SPAはそんなめんどくさいことは必要なく、クラアントに適切なHTMLとJSを渡すだけで解決します。SEOに弱く、また表示と操作がブラウザの性能に依存するので会員制のサイトで、その操作部分などSEOを気にしないサイトやwebアプリであればガンガン使ってもいいと思います。",{"title":79,"searchDepth":111,"depth":111,"links":2588},[2589],{"id":2505,"depth":101,"text":2506,"children":2590},[2591,2592,2593],{"id":2550,"depth":111,"text":2551},{"id":2563,"depth":111,"text":2564},{"id":2579,"depth":111,"text":2580},[1141],"2020-11-23",{},"\u002Farticles\u002Fwhat-ssg-ssr-spa",{"title":2497,"description":2497},"articles\u002Fwhat-ssg-ssr-spa",[2601,1483],"js","usMpTQM9YqI1I4JGQYjVe7BELZbLtw1Et1nerwUPpek",{"id":2604,"title":2605,"body":2606,"category":2706,"createdAt":2707,"description":2605,"extension":1143,"index":1144,"meta":2708,"navigation":193,"path":2709,"publish":193,"seo":2710,"series":1144,"seriesTitle":1144,"stem":2711,"tag":2712,"thumbnail":1144,"updatedAt":1144,"__hash__":2714},"articles\u002Farticles\u002Fie-check-for-mac.md","IEチェックなどに！MacのlocalhostをWindowsで開く（アクセス）する方法。",{"type":10,"value":2607,"toc":2700},[2608,2611,2614,2617,2620,2627,2630,2633,2636,2639,2642,2646,2649,2652,2655,2663,2666,2673,2681,2684,2687,2691,2694,2697],[13,2609,2610],{},"こんにちはjunです。IE対策してますか？まあ私はIE大嫌いですけど要件に入っているならば、対策は必須です。最近はIE11まででいいよ。という空気が漂っているので昔よりは対策はしやすいですが、やっぱり思うようなレイアウトにならいことが多いです。",[13,2612,2613],{},"そしてIEの厄介な点としてwindows環境でしか動かないという点です。web系のデザイナーしかり、エンジニアは環境構築の手間や情報量、性能からみてMacを使う人が多いです。私もMacを使用して開発しています。そのため、MacだとIEチェックが非常に面倒になります。",[13,2615,2616],{},"GitとかでWindows機にローカルリポジトリを置いてもいいのですが、やっぱり面倒です。できたら開発しながらリアルタイムで確認したいので、MacのlocalhostをWindowsで見れるようにする方法で解決します。",[25,2618,2619],{"id":2619},"macの設定を変更",[13,2621,2622,2623,2626],{},"まずlocalhostを他のPCでも共有できるようにするためには、WindowsとMacが同じ ",[1234,2624,2625],{},"LAN内にいるという条件"," があります。つまり同じWi-Fiを使用するということです。",[13,2628,2629],{},"互いに同じLANでつながっていることを確認したら、「システム環境設定」＞「共有」に進みます。",[13,2631,2632],{},"「共有」のなかに「インターネット共有」という欄がありますので、クリックします。",[66,2634],{":src":2635,":width":69},"'_mix\u002Flan-setting-on-mac.png'",[13,2637,2638],{},"そして「共有する接続経路」がWi-Fiであることを確認。違っていたらプルダウンから選びます。確認後、「インターネット共有」の左のチェックボックスをクリックして有効化します。",[13,2640,2641],{},"これでMac側の準備は完了です。",[25,2643,2645],{"id":2644},"windowsから早速接続","Windowsから早速接続",[13,2647,2648],{},"ではWindowsから接続するにはどうすればいいのか？上記の図のここに注目します。",[66,2650],{":src":2651,":width":69},"'_mix\u002Fscreen-2020-07-19-21.38.57-768x146.png'",[13,2653,2654],{},"そうです。「次のアドレスで〜〜」という所の「MacBook-Pro.local」をドメインのようにしてアクセスします。ここの名前は変えられるので複数人LAN内にいる場合などは変えるといいでしょう。",[13,2656,2657,2658],{},"実際に接続するときは「",[17,2659,2662],{"href":2660,"rel":2661},"http:\u002F\u002Fmacbook-pro.local%E3%80%8D%E3%81%A8%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%8B%E3%82%89%E6%8E%A5%E7%B6%9A%E3%81%97%E3%81%BE%E3%81%97%E3%82%87%E3%81%86%E3%80%82%EF%BC%88%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9%83%A8%E5%88%86%E3%81%AE%E5%A4%A7%E6%96%87%E5%AD%97%E3%81%AF%E8%87%AA%E5%8B%95%E7%9A%84%E3%81%AB%E5%B0%8F%E6%96%87%E5%AD%97%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82%EF%BC%89",[21],"http:\u002F\u002Fmacbook-pro.local」とブラウザから接続しましょう。（ドメイン部分の大文字は自動的に小文字になります。）",[25,2664,2665],{"id":2665},"実際に確認してみる",[13,2667,2668,2669,2672],{},"MAMPに",[81,2670,2671],{},"connecttest","というディレクトリを作成しておき、その中にindex.htmlを作成しておきます。htmlを開くと「Connection succeed!」という文章が出てきます。",[13,2674,2675,2676,2680],{},"忌まわしきIEを開いて上記の自分のMacを指すドメイン名を入力しましょう。MAMP上にあるので",[17,2677,2678],{"href":2678,"rel":2679},"http:\u002F\u002Fmacbook-pro.local:8888",[21]," とコロン（：）とデフォルトのポート8888に接続します。",[66,2682],{":src":2683,":width":69},"'_mix\u002Fscreenshot-60-1-768x512.png'",[66,2685],{":src":2686,":width":69},"'_mix\u002Fsh-61-768x512.png'",[25,2688,2690],{"id":2689},"mac内にieを入れるのは面倒","Mac内にIEを入れるのは面倒",[13,2692,2693],{},"以上がwindowsにMacを接続する方法です。めちゃくちゃ簡単なので早速やってみましょう。",[13,2695,2696],{},"この方法の場合、MacとWindows両方持っている人専用になります。一応Mac内にIE（Windows環境）を構築することはVirtual boxを用いて行うことができます。しかし、MacでWindowsOSを稼働させるのでパフォーマンスが遅く、また構築も非常に面倒です。",[13,2698,2699],{},"IEのためにWindowsを買うのも馬鹿らしいですが、会社とかでIE確認用のWindowsを持っていてもいいかもしれませんね",{"title":79,"searchDepth":111,"depth":111,"links":2701},[2702,2703,2704,2705],{"id":2619,"depth":101,"text":2619},{"id":2644,"depth":101,"text":2645},{"id":2665,"depth":101,"text":2665},{"id":2689,"depth":101,"text":2690},[1141],"2020-07-19",{},"\u002Farticles\u002Fie-check-for-mac",{"title":2605,"description":2605},"articles\u002Fie-check-for-mac",[2713],"network","K4ZixH0MRLLn2UJckWs_1Z9fmXvYqfxU3inpOS3CMmk",{"id":2716,"title":2717,"body":2718,"category":2955,"createdAt":2957,"description":2717,"extension":1143,"index":1144,"meta":2958,"navigation":193,"path":2959,"publish":193,"seo":2960,"series":1144,"seriesTitle":1144,"stem":2961,"tag":2962,"thumbnail":2963,"updatedAt":1144,"__hash__":2964},"articles\u002Farticles\u002Fstudyflow-lamp-byself.md","一人でLAMP環境を作れるようになるまでの勉強フロー。",{"type":10,"value":2719,"toc":2939},[2720,2723,2726,2729,2732,2760,2764,2767,2770,2773,2776,2779,2783,2786,2789,2792,2795,2798,2801,2804,2815,2818,2821,2824,2827,2847,2850,2853,2856,2859,2862,2865,2868,2871,2879,2882,2896,2899,2903,2906,2923,2926,2930,2933,2936],[13,2721,2722],{},"こんにちはjunです。私はフロントエンド エンジニアとして2019年の12月にWeb系会社に入りましたが、社員数が少ない上に複数のお取引先のサイトを保守しているのでサーバーの知識がまず必要になりました。",[13,2724,2725],{},"入社前まではサーバーというとXserverのようなレンタルサーバーにFTPを使ってファイルを転送したり、MAMPやXAMMPを用いてサーバーに似た環境を整えるといったぐらいの知識しかありませんでした。",[13,2727,2728],{},"ですがなんやかんや勉強したり仮想環境を用いて構築を行いました。おかげで入社3ヶ月目には実務上必要なLAMPサーバー構築作業ができるようになり、本番用のサーバーの構築も行い現在稼働しています。さらにインフラ部分とフロント部分との繋がり、またWebサービスの提供がどうやって行われるのかをよく理解できました。",[13,2730,2731],{},"今回の記事では実務上に求められるサーバー構築ができ、そしてある程度インフラ部分がわかるようになるため私が行った・意識したことを共有したいと思います。別途の記事でLAMP環境の構築方法とかvagrantのインストール方法を書きます。",[906,2733,2735,2739,2740,2743,2744,2746,2747,2750,2751,2753,2754,2756,2757,2759],{"className":2734},[1179,1180],[2736,2737,2738],"b",{},"LAMP","とはOSが",[2736,2741,2742],{},"L","inux系、Webサーバーに",[2736,2745,2546],{},"pache、DBサーバーに",[2736,2748,2749],{},"M","ySQL、サーバーサイド言語として",[2736,2752,2542],{},"HP\u002F",[2736,2755,2542],{},"erl\u002F",[2736,2758,2542],{},"ythonのどれかを入れたサーバー環境のことです。\n",[25,2761,2763],{"id":2762},"vagrantで仮想環境がお手軽","Vagrantで仮想環境がお手軽",[13,2765,2766],{},"まずプログラミングを習いたての方、例えばPHPを使ってDBと連携したり、JSライブラリのjqueryを用いてUIを学習するぐらいであればMAMPやXAMMPでOKです。ですがMAMPから次のステップに進むならばVPSやvagrantを用いてサーバーという物に触れた方が良いです。",[13,2768,2769],{},"VPSはお金がかかったりクラッシュさせると面倒なので、学習においてはvagrantがベストです。",[2096,2771,2772],{"id":2772},"vagrantとは",[13,2774,2775],{},"vagrantとはvirtual boxという仮想マシンを動かすためのツールです。仮想マシン、つまりMacのなかにWindowsの環境を構築したり、CentOS(商用のサーバーでよく使われるOS)を入れてサービスをリリースする環境を自分のPCに構築します。",[13,2777,2778],{},"仮想マシンを素で環境を構築しようとすると大変なので、vagrantというツールで「大体みんなこんな機能使うよね」という感じで設定してくれます。vagrantで構築するOSを指定すればあとはコマンドを使って自前サーバーを設定できるようになります。まあ、vagrant使えばVPSとか商用のサーバー環境を自分のPCで作れるんだなと思ってください。",[2096,2780,2782],{"id":2781},"どうして仮想環境が良いの","どうして仮想環境が良いの？",[13,2784,2785],{},"vagrantが良いのはdockerとは違って、実際のLAMP環境を構築する手順をローカルPC上で実演できるからです。最近はdockerでコードを書いてサーバー構築するという方法もありますが、それでもコマンドをポチポチ唱えてソフトやミドルウェア をインストールして..と構築する場面はまだ多いです",[13,2787,2788],{},"仮想環境なので例えミスって動かなくなっても壊して、再構築すれば良いだけです。そして仮想環境ですが、実際のサーバー構築と同じ動きをするのでサーバーのディレクトリ構成や、コマンド操作の練習が行いやすいです。",[13,2790,2791],{},"さらに言えば失敗できる環境なので、何かしらエラーが起きてそれを直してみるという経験は貴重です。サーバーでエラーということはwebページが全く表示されなかったり、サービスが停止する本番のサーバーでは失敗が許されません。その点を考えれば自由に失敗できる仮想環境は素晴らしいインフラ学習環境でもあります。",[2096,2793,2794],{"id":2794},"dockerだって失敗できるじゃないか",[13,2796,2797],{},"確かにdockerも同様に失敗できて、すぐに環境の再構築と破棄が可能です。しかしdockerを用いて環境構築するにはOSのディレクトリ構成とかApacheの設定の仕組みとかをインフラを最低限知っておかないと、ただの暗記コードを書くだけになります。",[13,2799,2800],{},"「このフォルダに〜〜の構成ファイルがあって、ここに設定しているのんだな〜」というのを理解するにはvagrantを用いて実際のサーバーを探検するのがベストです。",[25,2802,2803],{"id":2803},"webサービスが提供される本質を知る",[32,2805,2806,2809,2812],{},[35,2807,2808],{},"なぜ https:\u002F\u002F~~~~~とブラウザで検索するとwebサイトが見れるのですか？",[35,2810,2811],{},"ドキュメントルートとは何ですか？どこで設定できますか？",[35,2813,2814],{},"IPアドレス、ドメイン、ポートとはサーバーではどんな意味を持ちますか？",[13,2816,2817],{},"webサービスを提供するためにはインターネットに接続されたサーバーが必要です。ではサーバーに何をインストールして、何を設定して、どこにファイルをおけば自分が作ったアプリが実行され、他の人も使えるようになるのかをよく理解することです。",[13,2819,2820],{},"処理フローを追って、そしてインストールしたものがどのように相互作用しているのかを勉強するとインフラの知識の習得が早くなります。",[2096,2822,2823],{"id":2823},"まずはサーバーの基礎知識から",[13,2825,2826],{},"しかしインフラの知識がない中でソフトの相互作用とか全くわからないので、ソフトの前にサーバーの以下の知識を知るといいです。",[32,2828,2829,2832,2835,2838,2841,2844],{},[35,2830,2831],{},"OS（どんな時に使われるのか、有料なのか無料なのか程度）",[35,2833,2834],{},"ディレクトリ構成",[35,2836,2837],{},"ディレクトリ内の役割（ざっくり）",[35,2839,2840],{},"権限とユーザー（大事です）",[35,2842,2843],{},"SSHの方法と接続",[35,2845,2846],{},"コマンドの意味",[13,2848,2849],{},"まずはこれは基礎知識です。PC何に接続したvagrantまたはリモート上のVPSとかに接続する方法、サーバーの中を探求するためのコマンドは知っておかないとまずサーバーをいじれません笑",[13,2851,2852],{},"私も「vagrantを構築したのはいいけど、仮想サーバーはどこにあるん？？」となってました。",[2096,2854,2855],{"id":2855},"ソフトウェア等をいれる",[13,2857,2858],{},"MAMPなどをインストールするとApache,PHP,Mysqlが入っていました。しかしvagrantで構築するとそれらは入っていません。自分でコマンドを唱えてインストールします。しかしそれらをインストールして稼働できるようになれば、webサービスを構築できます。",[13,2860,2861],{},"「vagrant \u003Cソフトウェア名> インストール」と検索して方法を調べましょう。",[25,2863,2864],{"id":2864},"サーバー構築練習で意識すること",[2096,2866,2867],{"id":2867},"ググる際の注意",[13,2869,2870],{},"サーバー構築では絶対にエラーで悩みます。「Permission Deniedになる..」「PHPがインストールできない！」「500 Internal Server Error？もう知らん！」となるでしょう。エラーになった時は基本的にはググるのですが、そのググりの際に以下の点に注意しておくといいです。",[32,2872,2873,2876],{},[35,2874,2875],{},"バージョンによる差異に気をつける",[35,2877,2878],{},"検索した記事の発行日時に気をつける\n-その記事では何をしたいのか？どんな問題を解決しようとしてるのか、前提条件を確認する",[13,2880,2881],{},"特にバージョンによる差異は要注意です。自分がインストールしようとしているバージョンと、記事内で解説されているバージョンには気をつけましょう。特に",[32,2883,2884,2887,2890,2893],{},[35,2885,2886],{},"Apache2.2とApche2.4",[35,2888,2889],{},"CentOS6とCentOS7",[35,2891,2892],{},"MySQL5.7とそれ以前",[35,2894,2895],{},"PHP5系とPHP7系",[13,2897,2898],{},"こいつらには注意です。私もかなり苦労しました。バージョンごとに設定が大きく変わったりしているので、単に調べてコピペするだけでは解決しません。とりあえずどんなエラーが起きたのかログをメモして、バージョンに気をつけながら検索をしましょう。",[2096,2900,2902],{"id":2901},"まずはhelloworldhtmlを出してみよう","まずはHelloworld.htmlを出してみよう",[13,2904,2905],{},"ここでは主に勉強フローしか書いていませんが、",[32,2907,2908,2911,2914,2917,2920],{},[35,2909,2910],{},"vagrantとvirtual boxをいれる",[35,2912,2913],{},"OSを指定して仮想サーバーを立てる",[35,2915,2916],{},"必要なソフトウェア等をインストール",[35,2918,2919],{},"ドキュメントルート にHelloworld.htmlを入れておく",[35,2921,2922],{},"ブラウザでアクセスしてHelloworld.htmlを表示する",[13,2924,2925],{},"という流れができればインフラの理解はまず進みます。あとはセキュリティとか運用方法とか権限設定を学んでいくといいです。まずは最低限のインフラを整えて自分のwebサービスを表示・機能できるようにすると達成感と理解が半端じゃないです。",[25,2927,2929],{"id":2928},"lampはオワコンとか言われるが気にするな","LAMPはオワコンとか言われるが気にするな",[13,2931,2932],{},"確かに最近はLAMP通りの環境ではなく、WebサーバーにNginx、DBはPostgreSQL、ていうか「Dockerでよくね？」「AWS上での構築でしょ！」とも言われて「LAMPは古い。オワコンだ」とも言われます。",[13,2934,2935],{},"しかし私が思うのはこれらの話は「あくまでより良いwebサービスを提供するための技術選定のお話」であってインフラのことを知っている人たちのお話だと思います。インフラ初学者にとって、基本と言いますか原理的な部分としてはLAMP環境でサーバーの知識を知った方が敷居が低いと思います。",[13,2937,2938],{},"多分LAMPほど情報が多く、チュートリアルで分かりやすい環境構築はさほどないと思います。LAMPからサーバーのことを知っていけば、AWSなりDockerなどLAMP以外の環境構築もすぐに理解できると思います。",{"title":79,"searchDepth":111,"depth":111,"links":2940},[2941,2946,2950,2954],{"id":2762,"depth":101,"text":2763,"children":2942},[2943,2944,2945],{"id":2772,"depth":111,"text":2772},{"id":2781,"depth":111,"text":2782},{"id":2794,"depth":111,"text":2794},{"id":2803,"depth":101,"text":2803,"children":2947},[2948,2949],{"id":2823,"depth":111,"text":2823},{"id":2855,"depth":111,"text":2855},{"id":2864,"depth":101,"text":2864,"children":2951},[2952,2953],{"id":2867,"depth":111,"text":2867},{"id":2901,"depth":111,"text":2902},{"id":2928,"depth":101,"text":2929},[2956],"learning","2020-07-12",{},"\u002Farticles\u002Fstudyflow-lamp-byself",{"title":2717,"description":2717},"articles\u002Fstudyflow-lamp-byself",[2051],"_mix\u002Flamp.png","CKQOQrbqpmm4wvhwV_T8AhiuagWuX6LvExE6cLH8lcM",{"id":2966,"title":2967,"body":2968,"category":3927,"createdAt":3928,"description":2967,"extension":1143,"index":1144,"meta":3929,"navigation":193,"path":3930,"publish":193,"seo":3931,"series":1144,"seriesTitle":1144,"stem":3932,"tag":3933,"thumbnail":3935,"updatedAt":1144,"__hash__":3936},"articles\u002Farticles\u002Ftweet-check-laravel-jpb-scheduler.md","Larave 7.0でキュー・ジョブ・スケジューラー触ってみる。取得したツイートが存在するか毎夜毎夜チェックするジョブとスケジューラー。",{"type":10,"value":2969,"toc":3914},[2970,2973,2976,2979,2982,2985,2988,2996,2999,3002,3009,3020,3023,3026,3029,3032,3038,3041,3044,3047,3053,3056,3060,3063,3173,3183,3467,3470,3478,3481,3484,3614,3617,3624,3638,3641,3647,3650,3653,3660,3676,3683,3832,3842,3853,3859,3862,3869,3875,3878,3884,3890,3897,3903,3906,3909,3912],[13,2971,2972],{},"こんにちはjunです。仕事でLaravelを触ることになり必死にドキュメントを読んだりして対策をしています。フルスタックフレームワークと言われているほど、Laravelは機能が豊富なので想像していた物が作れて感動しています。",[13,2974,2975],{},"そこで今回はドキュメントをみてもイマイチパッとしない、ジョブ(Job)、キュー(Queue)、スケジューラー(Scheduler)を触りながら機能を確かめていきたいと思います。",[25,2977,2978],{"id":2978},"作りたい物",[13,2980,2981],{},"Laravelを用いて外部APIと通信して、取得したデータをDBに入れたりブラウザ上で操作するアプリケーションを作成していたときにある問題にぶつかりました。TwitterAPIを用いてツイートの情報を保存していたのですが、非公開もしくは削除されたため表示できないツイートがありました。",[13,2983,2984],{},"削除されたツイートを破棄するシステムを構築する必要があります。システム自体は簡単でDBから保存されたツイート一つ一つのIDを取得してTwitterAPIをを投げて、ツイートが存在するかの結果を確認すればいいだけです。スクレイピングするような感じです。しかしこの方法には課題があります。",[13,2986,2987],{},"保存されたツイートが大量にある場合、Twitterのサーバーに大量のリクエストを短時間に送信してしまい、Dosとなってしまいます。そのため各リクエスト毎に最低1秒程でも緩急を置く必要があります。その場合、その処理が終了する時間は保存されたツイート量によってはかなり長くなります。",[13,2989,2990,2991,2995],{},"大体、このような長くなりがちな処理はキューとして、サーバー上で非同期に行わせるのがベストです。そしてLarvelではジョブを簡単に実装できる",[17,2992,2994],{"href":2993,"target":1318},"https:\u002F\u002Freadouble.com\u002Flaravel\u002F7.x\u002Fja\u002Fscheduling.html","「タスクスケジュール」","という物があります。",[13,2997,2998],{},"今回は24時になると保存したツイートの存在を確認してくれる自動実行ジョブを構築したいと思います。",[25,3000,3001],{"id":3001},"タスクスケージューラーを理解する",[13,3003,3004,3008],{},[17,3005,3007],{"href":3006,"target":1318},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F7.x\u002Fscheduling","公式のドキュメント","を見てみましょう。おおよそですが実装の手順としては",[2432,3010,3011,3014,3017],{},[35,3012,3013],{},"ジョブの定義（実際の処理ロジック）をapp\u002Fjob\u002F 配下に作成する。",[35,3015,3016],{},"行うジョブやその実行時間は app\u002Fconsole\u002Fkernel.php に記述。",[35,3018,3019],{},"cronに毎分Larvelスケジューラーを実行させるコードを記述。",[13,3021,3022],{},"こんな感じです。細かく解説していきます。",[25,3024,3025],{"id":3025},"ジョブを作成する",[2096,3027,3028],{"id":3028},"キューを使えるようにする",[13,3030,3031],{},"まずはLaracelでジョブを実行できる環境を整えます。「キュー」に関するドキュメントを見ると以下のコマンドを唱えてジョブを記録するテーブルを作成します。",[74,3033,3036],{"className":3034,"code":3035,"language":1166},[1164],"php artisan queue:table\n\nphp artisan migrate\n",[81,3037,3035],{"__ignoreMap":79},[13,3039,3040],{},"このコマンドを唱えると「jobs」「failed_jobs」というテーブルが作成されます。ジョブとキューはセットで使われることが多いので上記のコマンドを唱えておきましょう",[2156,3042,3043],{"id":3043},"ジョブファイルを作成する",[13,3045,3046],{},"ジョブファイルは app\u002Fjob 配下に作成します。しかし最初はこのjobディレクトリは存在しないので以下のコマンドを唱えて、ジョブファイルのテンプレートとディレクトリを作ってもらいます。",[74,3048,3051],{"className":3049,"code":3050,"language":1166},[1164],"php artisan make:job YOUR_JOB_NAME\n",[81,3052,3050],{"__ignoreMap":79},[13,3054,3055],{},"「YOUR_JOB_NAME」の部分にジョブの名前を入力してください。他のmakeコマンドと同じ感じですね。このコマンドを唱えるとjobディレクトリとjobクラスが書かれたファイルが配下に作成されます。そのファイルにジョブの実行処理を書いていきます。",[2096,3057,3059],{"id":3058},"twitterにapiを送るジョブを記述","TwitterにAPIを送るジョブを記述",[13,3061,3062],{},"今回はジョブ名を「CheckNotFoundUrls」として作成します。ジョブファイルは作成するとまず下記のように書かれています。",[74,3064,3066],{"className":1199,"code":3065,"language":1201,"meta":79,"style":79},"\u003C?php\nnamespace App\\Jobs;\n\nuse Illuminate\\Bus\\Queueable;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Foundation\\Bus\\Dispatchable;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\n\nclass CheckNotFoundUrls implements ShouldQueue\n{\n    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n\n    public function __construct()\n    {\n        \u002F\u002F\n    }\n\n    public function handle()\n    {\n        \u002F\u002Fここに処理を記述\n    }\n}\n",[81,3067,3068,3072,3077,3081,3086,3091,3096,3101,3106,3110,3115,3119,3124,3128,3133,3138,3143,3147,3151,3156,3160,3165,3169],{"__ignoreMap":79},[84,3069,3070],{"class":86,"line":87},[84,3071,2218],{},[84,3073,3074],{"class":86,"line":101},[84,3075,3076],{},"namespace App\\Jobs;\n",[84,3078,3079],{"class":86,"line":111},[84,3080,194],{"emptyLinePlaceholder":193},[84,3082,3083],{"class":86,"line":128},[84,3084,3085],{},"use Illuminate\\Bus\\Queueable;\n",[84,3087,3088],{"class":86,"line":134},[84,3089,3090],{},"use Illuminate\\Contracts\\Queue\\ShouldQueue;\n",[84,3092,3093],{"class":86,"line":142},[84,3094,3095],{},"use Illuminate\\Foundation\\Bus\\Dispatchable;\n",[84,3097,3098],{"class":86,"line":156},[84,3099,3100],{},"use Illuminate\\Queue\\InteractsWithQueue;\n",[84,3102,3103],{"class":86,"line":161},[84,3104,3105],{},"use Illuminate\\Queue\\SerializesModels;\n",[84,3107,3108],{"class":86,"line":172},[84,3109,194],{"emptyLinePlaceholder":193},[84,3111,3112],{"class":86,"line":185},[84,3113,3114],{},"class CheckNotFoundUrls implements ShouldQueue\n",[84,3116,3117],{"class":86,"line":190},[84,3118,108],{},[84,3120,3121],{"class":86,"line":197},[84,3122,3123],{},"    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n",[84,3125,3126],{"class":86,"line":207},[84,3127,194],{"emptyLinePlaceholder":193},[84,3129,3130],{"class":86,"line":220},[84,3131,3132],{},"    public function __construct()\n",[84,3134,3135],{"class":86,"line":233},[84,3136,3137],{},"    {\n",[84,3139,3140],{"class":86,"line":254},[84,3141,3142],{},"        \u002F\u002F\n",[84,3144,3145],{"class":86,"line":259},[84,3146,131],{},[84,3148,3149],{"class":86,"line":264},[84,3150,194],{"emptyLinePlaceholder":193},[84,3152,3153],{"class":86,"line":274},[84,3154,3155],{},"    public function handle()\n",[84,3157,3158],{"class":86,"line":286},[84,3159,3137],{},[84,3161,3162],{"class":86,"line":297},[84,3163,3164],{},"        \u002F\u002Fここに処理を記述\n",[84,3166,3167],{"class":86,"line":302},[84,3168,131],{},[84,3170,3171],{"class":86,"line":318},[84,3172,2252],{},[13,3174,3175,3178,3179,3182],{},[81,3176,3177],{},"handle()","に処理内容を記述します。ジョブを実行するときはこのクラスを読み込み、ジョブインスタンスを作成し、",[81,3180,3181],{},"dispatch()","するとジョブが実行されます。まあ、とりあえずTwitterにAPIを飛ばすコードを書きます。コードは載せますが詳細の説明は省きます。",[74,3184,3186],{"className":1199,"code":3185,"language":1201,"meta":79,"style":79},"\u003C?php\nnamespace App\\Jobs;\n\n\u002F\u002FTwitter API ライブラリ\nuse Abraham\\TwitterOAuth\\TwitterOAuth;\n\nuse App\\Models\\Tweets; \u002F\u002F ツイート情報が格納されたテーブルに接続するモデル\nuse App\\Models\\JobReport;　\u002F\u002F 自作ジョブの結果を記録しておくテーブル\n\nuse Illuminate\\Bus\\Queueable;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Foundation\\Bus\\Dispatchable;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Illuminate\\Queue\\SerializesModels;\n\nclass CheckNotFoundUrls implements ShouldQueue\n{\n    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n\n    public function __construct()\n    {\n        \u002F\u002F\n    }\n\n    public function handle()\n    {\n        try{\n            \u002F\u002F ツイート情報があるテーブルからツイート情報（JSON）と必要なレコードを取得\n            $datas = Tweets::select('tweet_rawdata','id','user_id')->get();\n            $deletedTweetOwner = array();\n    \n            foreach($datas as $val){\n                \u002F\u002F'tweet_rawdata'カラムはjsonなので一旦decode。\n                \u002F\u002Fjsonからid_strというツイートIDを取得\n                $decodeTweetData = json_decode($val->tweet_rawdata);\n                $tweet_id = $decodeTweetData->id_str;\n\n                \u002F\u002FAPIに接続するための準備（ライブラリ）をして、APIを送信\n                $connection = new TwitterOAuth(env('TW_CONSUMER_KEY'), env('TW_CONSUMER_KEY_SECRET'), env('TW_ACCESS_TOKEN'), env('TW_ACCESS_TOKEN_SECRET'));\n                $connection->setTimeouts(10, 15);\n                $content = $connection->get(\"statuses\u002Fshow\",['id'=>intval($tweet_id)]);\n    \n                \u002F\u002F請求したツイートIDのツイートが存在しないという結果ならばレコードのIDを $deletedTweetOwnerにユーザーIDを記録\n                if(property_exists($content,'errors')){\n                    switch($content->errors[0]->code){\n                        case 144:\n                            Tweets::destroy($val->id);\n                            array_push($deletedTweetOwner,$val->id);\n                        break;\n                    }\n                }\n                \u002F\u002FDosにならないように1秒止める\n                sleep(1.0);\n            }\n  \n        }catch(\\Exception $e){\n            \u002F\u002F処理が失敗したら管理者用のレポートにエラ〜メッセージを記録する。\n            JobReport::reportException(0,$e->getMessage());\n        }\n    }\n}\n",[81,3187,3188,3192,3196,3200,3205,3210,3214,3219,3224,3228,3232,3236,3240,3244,3248,3252,3256,3260,3264,3268,3272,3276,3280,3284,3288,3292,3296,3301,3306,3311,3316,3321,3326,3331,3336,3341,3346,3350,3355,3360,3365,3370,3374,3379,3384,3389,3394,3399,3404,3409,3414,3419,3424,3429,3434,3439,3444,3449,3454,3459,3463],{"__ignoreMap":79},[84,3189,3190],{"class":86,"line":87},[84,3191,2218],{},[84,3193,3194],{"class":86,"line":101},[84,3195,3076],{},[84,3197,3198],{"class":86,"line":111},[84,3199,194],{"emptyLinePlaceholder":193},[84,3201,3202],{"class":86,"line":128},[84,3203,3204],{},"\u002F\u002FTwitter API ライブラリ\n",[84,3206,3207],{"class":86,"line":134},[84,3208,3209],{},"use Abraham\\TwitterOAuth\\TwitterOAuth;\n",[84,3211,3212],{"class":86,"line":142},[84,3213,194],{"emptyLinePlaceholder":193},[84,3215,3216],{"class":86,"line":156},[84,3217,3218],{},"use App\\Models\\Tweets; \u002F\u002F ツイート情報が格納されたテーブルに接続するモデル\n",[84,3220,3221],{"class":86,"line":161},[84,3222,3223],{},"use App\\Models\\JobReport;　\u002F\u002F 自作ジョブの結果を記録しておくテーブル\n",[84,3225,3226],{"class":86,"line":172},[84,3227,194],{"emptyLinePlaceholder":193},[84,3229,3230],{"class":86,"line":185},[84,3231,3085],{},[84,3233,3234],{"class":86,"line":190},[84,3235,3090],{},[84,3237,3238],{"class":86,"line":197},[84,3239,3095],{},[84,3241,3242],{"class":86,"line":207},[84,3243,3100],{},[84,3245,3246],{"class":86,"line":220},[84,3247,3105],{},[84,3249,3250],{"class":86,"line":233},[84,3251,194],{"emptyLinePlaceholder":193},[84,3253,3254],{"class":86,"line":254},[84,3255,3114],{},[84,3257,3258],{"class":86,"line":259},[84,3259,108],{},[84,3261,3262],{"class":86,"line":264},[84,3263,3123],{},[84,3265,3266],{"class":86,"line":274},[84,3267,194],{"emptyLinePlaceholder":193},[84,3269,3270],{"class":86,"line":286},[84,3271,3132],{},[84,3273,3274],{"class":86,"line":297},[84,3275,3137],{},[84,3277,3278],{"class":86,"line":302},[84,3279,3142],{},[84,3281,3282],{"class":86,"line":318},[84,3283,131],{},[84,3285,3286],{"class":86,"line":334},[84,3287,194],{"emptyLinePlaceholder":193},[84,3289,3290],{"class":86,"line":339},[84,3291,3155],{},[84,3293,3294],{"class":86,"line":352},[84,3295,3137],{},[84,3297,3298],{"class":86,"line":365},[84,3299,3300],{},"        try{\n",[84,3302,3303],{"class":86,"line":378},[84,3304,3305],{},"            \u002F\u002F ツイート情報があるテーブルからツイート情報（JSON）と必要なレコードを取得\n",[84,3307,3308],{"class":86,"line":383},[84,3309,3310],{},"            $datas = Tweets::select('tweet_rawdata','id','user_id')->get();\n",[84,3312,3313],{"class":86,"line":388},[84,3314,3315],{},"            $deletedTweetOwner = array();\n",[84,3317,3318],{"class":86,"line":404},[84,3319,3320],{},"    \n",[84,3322,3323],{"class":86,"line":417},[84,3324,3325],{},"            foreach($datas as $val){\n",[84,3327,3328],{"class":86,"line":429},[84,3329,3330],{},"                \u002F\u002F'tweet_rawdata'カラムはjsonなので一旦decode。\n",[84,3332,3333],{"class":86,"line":444},[84,3334,3335],{},"                \u002F\u002Fjsonからid_strというツイートIDを取得\n",[84,3337,3338],{"class":86,"line":449},[84,3339,3340],{},"                $decodeTweetData = json_decode($val->tweet_rawdata);\n",[84,3342,3343],{"class":86,"line":454},[84,3344,3345],{},"                $tweet_id = $decodeTweetData->id_str;\n",[84,3347,3348],{"class":86,"line":464},[84,3349,194],{"emptyLinePlaceholder":193},[84,3351,3352],{"class":86,"line":475},[84,3353,3354],{},"                \u002F\u002FAPIに接続するための準備（ライブラリ）をして、APIを送信\n",[84,3356,3357],{"class":86,"line":503},[84,3358,3359],{},"                $connection = new TwitterOAuth(env('TW_CONSUMER_KEY'), env('TW_CONSUMER_KEY_SECRET'), env('TW_ACCESS_TOKEN'), env('TW_ACCESS_TOKEN_SECRET'));\n",[84,3361,3362],{"class":86,"line":526},[84,3363,3364],{},"                $connection->setTimeouts(10, 15);\n",[84,3366,3367],{"class":86,"line":538},[84,3368,3369],{},"                $content = $connection->get(\"statuses\u002Fshow\",['id'=>intval($tweet_id)]);\n",[84,3371,3372],{"class":86,"line":553},[84,3373,3320],{},[84,3375,3376],{"class":86,"line":564},[84,3377,3378],{},"                \u002F\u002F請求したツイートIDのツイートが存在しないという結果ならばレコードのIDを $deletedTweetOwnerにユーザーIDを記録\n",[84,3380,3381],{"class":86,"line":577},[84,3382,3383],{},"                if(property_exists($content,'errors')){\n",[84,3385,3386],{"class":86,"line":621},[84,3387,3388],{},"                    switch($content->errors[0]->code){\n",[84,3390,3391],{"class":86,"line":626},[84,3392,3393],{},"                        case 144:\n",[84,3395,3396],{"class":86,"line":631},[84,3397,3398],{},"                            Tweets::destroy($val->id);\n",[84,3400,3401],{"class":86,"line":646},[84,3402,3403],{},"                            array_push($deletedTweetOwner,$val->id);\n",[84,3405,3406],{"class":86,"line":657},[84,3407,3408],{},"                        break;\n",[84,3410,3411],{"class":86,"line":668},[84,3412,3413],{},"                    }\n",[84,3415,3416],{"class":86,"line":681},[84,3417,3418],{},"                }\n",[84,3420,3421],{"class":86,"line":694},[84,3422,3423],{},"                \u002F\u002FDosにならないように1秒止める\n",[84,3425,3426],{"class":86,"line":707},[84,3427,3428],{},"                sleep(1.0);\n",[84,3430,3431],{"class":86,"line":725},[84,3432,3433],{},"            }\n",[84,3435,3436],{"class":86,"line":741},[84,3437,3438],{},"  \n",[84,3440,3441],{"class":86,"line":746},[84,3442,3443],{},"        }catch(\\Exception $e){\n",[84,3445,3446],{"class":86,"line":751},[84,3447,3448],{},"            \u002F\u002F処理が失敗したら管理者用のレポートにエラ〜メッセージを記録する。\n",[84,3450,3451],{"class":86,"line":769},[84,3452,3453],{},"            JobReport::reportException(0,$e->getMessage());\n",[84,3455,3456],{"class":86,"line":785},[84,3457,3458],{},"        }\n",[84,3460,3461],{"class":86,"line":798},[84,3462,131],{},[84,3464,3465],{"class":86,"line":814},[84,3466,2252],{},[13,3468,3469],{},"ちなみにTwitterへのAPIアクセスには Abraham\\TwitterOAuth というTwttiter公式が紹介するサードパーティライブラリを使用しています。composerで読み込んでuseすればすぐに使え、上記のようにouthインスタンスを作成するだけで使えます。簡単なのでLarvelでTwitterAPIを使う時などにおすすめです。",[906,3471,3473,3474],{"className":3472},[1179,2452],"\n    ",[17,3475,3477],{"href":3476,"target":1318},"https:\u002F\u002Fgithub.com\u002Fabraham\u002Ftwitteroauth","GITHUB：abraham\u002Ftwitteroauth\n    ",[13,3479,3480],{},"とりあえずこれでジョブは完成しました。試しにチェックをしてみたいですが、なんとかしてこのジョブを実行する必要があります。しかしLaravelには定義したジョブを実行するコマンドが見当たりません。（もしかしたら見落としてるだけかも）",[13,3482,3483],{},"コマンドでジョブを叩いて実行したいので、以下のカスタムコマンドを自作します。",[74,3485,3487],{"className":1199,"code":3486,"language":1201,"meta":79,"style":79},"\u003C?php\n\nnamespace App\\Console\\Commands;\n\nuse Illuminate\\Console\\Command;\n\nclass DispatchJob extends Command\n{\n    protected $signature = 'job:dispatch {job}';\n\n    \u002F**\n     * The console command description.\n     *\n     * @var string\n     *\u002F\n    protected $description = 'Dispatch job';\n\n    public function __construct()\n    {\n        parent::__construct();\n    }\n\n    public function handle()\n    {\n        $class = '\\\\App\\\\Jobs\\\\' . $this->argument('job');\n        dispatch(new $class());\n    }\n}\n",[81,3488,3489,3493,3497,3502,3506,3511,3515,3520,3524,3529,3533,3538,3543,3548,3553,3558,3563,3567,3571,3575,3580,3584,3588,3592,3596,3601,3606,3610],{"__ignoreMap":79},[84,3490,3491],{"class":86,"line":87},[84,3492,2218],{},[84,3494,3495],{"class":86,"line":101},[84,3496,194],{"emptyLinePlaceholder":193},[84,3498,3499],{"class":86,"line":111},[84,3500,3501],{},"namespace App\\Console\\Commands;\n",[84,3503,3504],{"class":86,"line":128},[84,3505,194],{"emptyLinePlaceholder":193},[84,3507,3508],{"class":86,"line":134},[84,3509,3510],{},"use Illuminate\\Console\\Command;\n",[84,3512,3513],{"class":86,"line":142},[84,3514,194],{"emptyLinePlaceholder":193},[84,3516,3517],{"class":86,"line":156},[84,3518,3519],{},"class DispatchJob extends Command\n",[84,3521,3522],{"class":86,"line":161},[84,3523,108],{},[84,3525,3526],{"class":86,"line":172},[84,3527,3528],{},"    protected $signature = 'job:dispatch {job}';\n",[84,3530,3531],{"class":86,"line":185},[84,3532,194],{"emptyLinePlaceholder":193},[84,3534,3535],{"class":86,"line":190},[84,3536,3537],{},"    \u002F**\n",[84,3539,3540],{"class":86,"line":197},[84,3541,3542],{},"     * The console command description.\n",[84,3544,3545],{"class":86,"line":207},[84,3546,3547],{},"     *\n",[84,3549,3550],{"class":86,"line":220},[84,3551,3552],{},"     * @var string\n",[84,3554,3555],{"class":86,"line":233},[84,3556,3557],{},"     *\u002F\n",[84,3559,3560],{"class":86,"line":254},[84,3561,3562],{},"    protected $description = 'Dispatch job';\n",[84,3564,3565],{"class":86,"line":259},[84,3566,194],{"emptyLinePlaceholder":193},[84,3568,3569],{"class":86,"line":264},[84,3570,3132],{},[84,3572,3573],{"class":86,"line":274},[84,3574,3137],{},[84,3576,3577],{"class":86,"line":286},[84,3578,3579],{},"        parent::__construct();\n",[84,3581,3582],{"class":86,"line":297},[84,3583,131],{},[84,3585,3586],{"class":86,"line":302},[84,3587,194],{"emptyLinePlaceholder":193},[84,3589,3590],{"class":86,"line":318},[84,3591,3155],{},[84,3593,3594],{"class":86,"line":334},[84,3595,3137],{},[84,3597,3598],{"class":86,"line":339},[84,3599,3600],{},"        $class = '\\\\App\\\\Jobs\\\\' . $this->argument('job');\n",[84,3602,3603],{"class":86,"line":352},[84,3604,3605],{},"        dispatch(new $class());\n",[84,3607,3608],{"class":86,"line":365},[84,3609,131],{},[84,3611,3612],{"class":86,"line":378},[84,3613,2252],{},[13,3615,3616],{},"このstack overflowの記事を引用しています。ありがとうございます。",[906,3618,3473,3620],{"className":3619},[1179,2452],[17,3621,3623],{"href":3622,"target":1318},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F43357472\u002Fhow-to-manually-run-a-laravel-lumen-job-using-command-line\u002F51045851","StackOverflow How to manually run a laravel\u002Flumen job using command line\n    ",[13,3625,3626,3627,3630,3631,3634,3635,3637],{},"このファイルをapp\u002FConsole\u002FCommands 配下に作成します。Console\u002FCommands 配下では",[81,3628,3629],{},"php artisan"," を用いたコマンドを自分で作成することができます。ここでは ",[81,3632,3633],{},"php artisan job:dispatch JOB_NAME"," と唱えればJOB_NAMEで書かれたジョブクラスの",[81,3636,3177],{},"を実行してくれます。",[13,3639,3640],{},"これを用いて以下のように唱えます。",[74,3642,3645],{"className":3643,"code":3644,"language":1166},[1164],"php artisan job:dispatch CheckNotFoundUrls\n",[81,3646,3644],{"__ignoreMap":79},[13,3648,3649],{},"するとprintなどを一時的につけて、結果を見ると無事に404のツイートの数が検知され、削除を実行してくれました。",[2096,3651,3652],{"id":3652},"ジョブが実行される時間を定める",[13,3654,3655,3656,3659],{},"それでは次に上で作成したジョブをスケジューラーに登録します。スケジューラーで実行させるジョブは ",[81,3657,3658],{},"app\u002FConsole\u002FKernel.php"," にジョブインスタンスを作って実行します。",[13,3661,3662,3664,3665,3668,3669,3672,3673,3675],{},[81,3663,3658],{}," には ",[81,3666,3667],{},"schedule()"," というメソッドがあり ",[81,3670,3671],{},"php artisan run:schedule"," を実行すると ",[81,3674,3667],{}," が実行されます。",[13,3677,3678,3679,3682],{},"先ほどのジョブクラスをこの",[81,3680,3681],{},"Kernel.php","で使えるようにして、ジョブインスタンスを作成します。",[74,3684,3686],{"className":1199,"code":3685,"language":1201,"meta":79,"style":79},"\u003C?php\n\nnamespace App\\Console;\n\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse App\\Jobs\\CheckNotFoundUrls; \u002F\u002Fここで先ほど作成したジョブクラス\nuse DateTime;\n\nclass Kernel extends ConsoleKernel\n{\n    \u002F**\n     * The Artisan commands provided by your application.\n     *\n     * @var array\n     *\u002F\n    protected $commands = [\n        \u002F\u002F\n    ];\n\n    \u002F**\n     * Define the application's command schedule.\n     *\n     * @param  \\Illuminate\\Console\\Scheduling\\Schedule  $schedule\n     * @return void\n     *\u002F\n    protected function schedule(Schedule $schedule)\n    {\n        \u002F\u002F実行するjobをキューに投入する。そして実行時間を設定。\n        $schedule->job(new CheckNotFoundUrls(),'checkUrl')->diary()\n    }\n}\n",[81,3687,3688,3692,3696,3701,3705,3710,3715,3720,3725,3729,3734,3738,3742,3747,3751,3756,3760,3765,3769,3774,3778,3782,3787,3791,3796,3801,3805,3810,3814,3819,3824,3828],{"__ignoreMap":79},[84,3689,3690],{"class":86,"line":87},[84,3691,2218],{},[84,3693,3694],{"class":86,"line":101},[84,3695,194],{"emptyLinePlaceholder":193},[84,3697,3698],{"class":86,"line":111},[84,3699,3700],{},"namespace App\\Console;\n",[84,3702,3703],{"class":86,"line":128},[84,3704,194],{"emptyLinePlaceholder":193},[84,3706,3707],{"class":86,"line":134},[84,3708,3709],{},"use Illuminate\\Console\\Scheduling\\Schedule;\n",[84,3711,3712],{"class":86,"line":142},[84,3713,3714],{},"use Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\n",[84,3716,3717],{"class":86,"line":156},[84,3718,3719],{},"use App\\Jobs\\CheckNotFoundUrls; \u002F\u002Fここで先ほど作成したジョブクラス\n",[84,3721,3722],{"class":86,"line":161},[84,3723,3724],{},"use DateTime;\n",[84,3726,3727],{"class":86,"line":172},[84,3728,194],{"emptyLinePlaceholder":193},[84,3730,3731],{"class":86,"line":185},[84,3732,3733],{},"class Kernel extends ConsoleKernel\n",[84,3735,3736],{"class":86,"line":190},[84,3737,108],{},[84,3739,3740],{"class":86,"line":197},[84,3741,3537],{},[84,3743,3744],{"class":86,"line":207},[84,3745,3746],{},"     * The Artisan commands provided by your application.\n",[84,3748,3749],{"class":86,"line":220},[84,3750,3547],{},[84,3752,3753],{"class":86,"line":233},[84,3754,3755],{},"     * @var array\n",[84,3757,3758],{"class":86,"line":254},[84,3759,3557],{},[84,3761,3762],{"class":86,"line":259},[84,3763,3764],{},"    protected $commands = [\n",[84,3766,3767],{"class":86,"line":264},[84,3768,3142],{},[84,3770,3771],{"class":86,"line":274},[84,3772,3773],{},"    ];\n",[84,3775,3776],{"class":86,"line":286},[84,3777,194],{"emptyLinePlaceholder":193},[84,3779,3780],{"class":86,"line":297},[84,3781,3537],{},[84,3783,3784],{"class":86,"line":302},[84,3785,3786],{},"     * Define the application's command schedule.\n",[84,3788,3789],{"class":86,"line":318},[84,3790,3547],{},[84,3792,3793],{"class":86,"line":334},[84,3794,3795],{},"     * @param  \\Illuminate\\Console\\Scheduling\\Schedule  $schedule\n",[84,3797,3798],{"class":86,"line":339},[84,3799,3800],{},"     * @return void\n",[84,3802,3803],{"class":86,"line":352},[84,3804,3557],{},[84,3806,3807],{"class":86,"line":365},[84,3808,3809],{},"    protected function schedule(Schedule $schedule)\n",[84,3811,3812],{"class":86,"line":378},[84,3813,3137],{},[84,3815,3816],{"class":86,"line":383},[84,3817,3818],{},"        \u002F\u002F実行するjobをキューに投入する。そして実行時間を設定。\n",[84,3820,3821],{"class":86,"line":388},[84,3822,3823],{},"        $schedule->job(new CheckNotFoundUrls(),'checkUrl')->diary()\n",[84,3825,3826],{"class":86,"line":404},[84,3827,131],{},[84,3829,3830],{"class":86,"line":417},[84,3831,2252],{},[13,3833,3834,3837,3838,3841],{},[81,3835,3836],{},"$schedule->()","の第一引数にジョブインスタンス、第二引数にキュー名を入れます。キューというのは実行登録したジョブを一時的にためておく場所のことです。私もよくわかっていませんが、ジョブの実行数を制限したり、どの順に実行をしていくかなども決められるそうです。とりあえず今回は404チェック用のジョブキューとして ",[81,3839,3840],{},"checkUrl"," としておきます。",[13,3843,3844,3845,3848,3849,3852],{},"そして行末に",[81,3846,3847],{},"diary()","などがありますが、",[17,3850,1824],{"href":3851,"target":1318},"https:\u002F\u002Flaravel.com\u002Fdocs\u002F7.x\u002Fscheduling#schedule-frequency-options","の通り実行する時間を指定できます。cron通りの時刻設定もできますし、1日ごと、週末ごと、月初に１回などよくある時間設定はメソッドを指定すれば設定できます。",[13,3854,3855,3856,3858],{},"上記のコードでは",[81,3857,3847],{},"なので毎日0時になるとジョブが実行されます。",[2096,3860,3861],{"id":3861},"cronを設定する",[13,3863,3864,3865,3868],{},"ジョブを定義し、その時間も定めたのでもう自動で実行されそうな気もしますが、これだけでは動きません。指定の時間になったら特定のコマンドつまり",[81,3866,3867],{},"php artisan schedule:run"," を唱えてphpを動かさないといけません。",[13,3870,3871,3872,3874],{},"ここはサーバー(Linux)のcronというものを用いて、常に",[81,3873,3867],{}," を唱えるように設定します。cronとは時間設定基づいてコマンドを定期的に唱えてくれるUNIX系のOSに入っているプログラムです。",[13,3876,3877],{},"cronに以下の設定を記述します。",[74,3879,3882],{"className":3880,"code":3881,"language":1166},[1164],"~ $ crontab -e #これでcronの設定ファイルを編集できます。\n",[81,3883,3881],{"__ignoreMap":79},[74,3885,3888],{"className":3886,"code":3887,"language":1166},[1164],"* * * * * cd \u002Fpath-to-your-project && php artisan schedule:run >> \u002Fdev\u002Fnull 2>&1\n",[81,3889,3887],{"__ignoreMap":79},[13,3891,3892,3893,3896],{},"行の最初にある",[81,3894,3895],{},"(* * * * *)","は時間の設定をしています。この書き方の場合は毎分実行するようになります。つまり毎分Laravelのスケジューラーを叩いているだけなんです。",[13,3898,3899,3902],{},[81,3900,3901],{},"cd \u002Fpath-to-your-project"," はLaravelプロジェクトのルートへ移動するという意味です。そしてプロジェクトルートでスケジュールを実行します。",[25,3904,3905],{"id":3905},"以上でスケジューラー実装完了",[13,3907,3908],{},"以上でスケジューラーの実装が完了しました。今回のような404を毎日チェックしたり、定期的に時間を設定して行う処理などをアプリ内で行う機能を実装する時に便利です。",[13,3910,3911],{},"ユーザーごとに実行するかどうか、いつ実行するかの情報を格納しておくことで一般ユーザーが定期的な処理を行う処理を設定することもできます。",[95,3913,1287],{},{"title":79,"searchDepth":111,"depth":111,"links":3915},[3916,3917,3918,3926],{"id":2978,"depth":101,"text":2978},{"id":3001,"depth":101,"text":3001},{"id":3025,"depth":101,"text":3025,"children":3919},[3920,3923,3924,3925],{"id":3028,"depth":111,"text":3028,"children":3921},[3922],{"id":3043,"depth":128,"text":3043},{"id":3058,"depth":111,"text":3059},{"id":3652,"depth":111,"text":3652},{"id":3861,"depth":111,"text":3861},{"id":3905,"depth":101,"text":3905},[2044],"2020-07-11",{},"\u002Farticles\u002Ftweet-check-laravel-jpb-scheduler",{"title":2967,"description":2967},"articles\u002Ftweet-check-laravel-jpb-scheduler",[3934],"laravel","_mix\u002Flaravel-schedular-768x768.jpg","4qvDlezjHnhP7U6K9l6HlE2ilA1kcrRrD19TfDyuMNc",1780987153784]