hi_lili
@hi_lili

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Laravel ajaxで非同期処理を行いたい

Q&A

Closed

解決したいこと

Ajaxで検索処理を非同期処理で動作させたい

発生している問題・エラー

コンソールにいて404 Not Found のエラーが出ている状態

スクリーンショット 2024-02-27 15.47.18.png

index.blade.php
 <form action="{{ route('products.index') }}" method="GET" class="row g-3" id="search-form">
        <div class="col-sm-12 col-md-3">
            <input type="text" name="search" class="form-control" placeholder="商品名" value="{{ request('search') }}" id="search">
        </div>
        <div class="col-sm-12 col-md-3">
            <select name="company_id" class="form-control" id="company">
                <option value="">全て</option>
                @foreach($company_lists as $key => $value)
                <option value="{{ $key }}" @if($company == $key) selected="selected" @endif>{{ $value }}</option>
                @endforeach
            </select>
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="price_min" class="form-control" placeholder="価格の下限" value="{{ request('price_min') }}">
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="price_max" class="form-control" placeholder="価格の上限" value="{{ request('price_max') }}">
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="stock_min" class="form-control" placeholder="在庫数の下限" value="{{ request('stock_min') }}">
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="stock_max" class="form-control" placeholder="在庫数の上限" value="{{ request('stock_max') }}">
        </div>
        <div class="col-sm-12 col-md-1">
            <button class="btn btn-outline-secondary" type="submit">絞り込み</button>
        </div>
    </form>

web.php
Route::get('/products', [App\Http\Controllers\ProductController::class, 'index'])->name('products.index');
Route::get('/products/search', [App\Http\Controllers\ProductController::class, 'search'])->name('products.search');
search.js
$(function() {
  // 検索ボタンのクリックイベントを設定
  $('#search-form').submit(function(event) {
    event.preventDefault();
    
    // CSRFトークンを取得
    var csrfToken = $('meta[name="csrf-token"]').attr('content');
    
    // AJAXリクエストを作成
    $.ajax({
      url: "/products/search", // 検索処理を行うAPIのURL
      type: 'GET',
      dataType: "json",
      data: {
        search: $('#search').val(),
        company_id: $('#company').val(),
        priceMin: $('input[name="price_min"]').val(),
        priceMax: $('input[name="price_max"]').val(),
        stockMin: $('input[name="stock_min"]').val(),
        stockMax: $('input[name="stock_max"]').val(),
      }
    }).done((response) => {
        console.log(response);
        alert('ajaxが成功しました');
        var $result  = $('#search-results');
        $result.empty();
        $.each(response.products.data, function(index, product){
          var html = `
          <tr>
          <td>${product.product_name}</td>
          <td>${product.company.company_name}</td>
          <td>${product.price}</td>
          <td>${product.stock}</td>
          <td>${product.comment}</td>
          <td><img src="${product.img_path}" alt="商品画像" width="100"></td>
      </tr>`;
      $result = append(html);
        })
      }).fail((error) => {
        console.log('AJAXが失敗しました');
        console.log('jqXHR:' + jqXHR.status);
        console.log('textStatus:' + textStatus);
        console.log('errorThrown:' + errorThrown);
        console.log('url:' + url);
      });
    });
  });

ProductController.php
 public function index(Request $request){

        $products = Product::search($request->search, $request->company_id, $request->price_min, $request->price_max, $request->stock_min, $request->stock_max)
        ->paginate(10);
        $company_lists = Company::pluck('company_name', 'id');
        $company = $request->company_id;
    
        return view('products.index', compact('products', 'company_lists', 'company'));
    }

    public function search(Request $request)
    {
        $products = Product::search($request->search, $request->company_id, $request->price_min, $request->price_max, $request->stock_min, $request->stock_max)
        ->paginate(10);
        $company_lists = Company::pluck('company_name', 'id');
        $company_id = $request->company_id;
        dd($company_id); 
    
        return response()->json([
            'products' => $products,
            'company_id' => $company_id,
            'company_lists' => $company_lists
        ]);
    }
app.blade.php
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
    <script src="{{asset('js/search.js')}}"></script>

自分で試したこと

404 Not fonudなので、ルーティングに誤りがあると思い見直しましたが、
誤っている部分がわからなかったです:cold_sweat:

スーパーリロードを行ったりしてみましたが、
自身の知識ではわからず、質問をさせていただきます。
何卒よろしくお願いいたします。:bow_tone2:

0

4Answer

Comments

  1. @hi_lili

    Questioner

    @yp_s10
    ご教授ありがとうございます。
    そちらも一度実施済みでしたが、再度行いました。
    しかし、挙動が変わらない状態となります:sob:

HTTP 404 Not Found というのは、クライアントからの要求は web サーバーまで届いて、サーバーは要求された url に指定されるリソースを探したが、見つからないと言って返した応答です。

原因のほとんどは指定した url が間違っているということです (多分これ⇒ url: "/products/search" が間違っている)。まずは、そのあたりをチェックしてください。

0Like

Comments

  1. @hi_lili

    Questioner

    @SurferOnWww
    ご教授ありがとうございます。
    私もそこを疑い修正を行いましたが、
    どうしても挙動が変わらない状態となります・・・:bow_tone1:

  2. Laravel は違うのかもしれませんが・・・

    コントローラー名が ProductController なのですか? だとすると url は /products/search ではなくて(products の s が余分) /product/search としないとダメとか? (ちなみに、ASP.NET MVC はそうしないとダメです)

route:listコマンドで実際に適用されているルート設定は確認されたでしょうか?
開発者ツールのNetworkタブでレスポンスの詳細を見ることはできますか?
AJAXでリクエストしているURLをブラウザで開くと、やはり404エラーになるでしょうか?

参考

0Like

Comments

  1. @hi_lili

    Questioner

    @blue32a
    返事が遅れてしまい申し訳ありません。
    下記画像のエラーが出ておりました。。。
    スクリーンショット 2024-03-10 17.31.56.png

    API\ProductControllerを作成し、
    下記を記述しましたがエラーの内容は変わらずとなります。

    API\ProductController
    <?php
    
    namespace App\Http\Controllers\API;
    
    use App\Models\Product;
    use App\Models\Company;
    use Illuminate\Http\Request;
    use App\Http\Requests\ProductRequest;
    use Illuminate\Support\Facades\Storage;
    use Illuminate\Support\Facades\DB;
    use App\Jobs\SearchProductJob;
    
    class ProductController extends Controller
    {
    
        public function index(Request $request){
    
            $products = Product::search($request->search, $request->company_id, $request->price_min, $request->price_max, $request->stock_min, $request->stock_max)
            ->paginate(10);
            $company_lists = Company::pluck('company_name', 'id');
            $company = $request->company_id;
        
            return view('products.index', compact('products', 'company_lists', 'company'));
        }
    
        public function search(Request $request)
        {
            $products = Product::search($request->search, $request->company_id, $request->price_min, $request->price_max, $request->stock_min, $request->stock_max)
            ->paginate(10);
            $company_lists = Company::pluck('company_name', 'id');
            $company_id = $request->company_id;
            dd($company_id); 
        
            return response()->json([
                'products' => $products,
                'company_id' => $company_id,
                'company_lists' => $company_lists
            ]);
        }
    
        public function create(){
    
            $companies = Company::all();
            return view('products.create', compact('companies'));
        }
    
        public function store(ProductRequest $request){
    
            DB::beginTransaction();
            try {
                Product::createProduct($request->all());
                DB::commit();
            } catch (\Exception $e) {
                DB::rollback();
                return back();
            }
            return redirect('products');
        }
        
    
        public function show(Product $product){
    
            return view('products.show', ['product' => $product]);
        }
    
        public function edit(Product $product){
    
            $companies = Company::all();
            return view('products.edit', compact('product', 'companies'));
        }
    
        public function update(ProductRequest $request, Product $product){
    
            $product->fill($request->all());
           
            if(isset($product->img_path)){
                $filePath = $product->img_path;
                \Storage::disk('public')->delete($filePath);
            }
          
            if($request->hasFile('img_path')){
            $filename = $request->img_path->getClientOriginalName();
            $filePath = $request->img_path->storeAs('products', $filename, 'public');
            $product->img_path = '/storage/' . $filePath;    
            }
            
            $product->save();
            return redirect()->route('products.index');
    
        }
    
        public function destroy(Product $product){
    
            $product->delete();
            return redirect('/products');
        }
        
        
    
    }
    

    api.phpにもルーティングを記述

    api.php
    <?php
    
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Route;
    
    Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
        return $request->user();
    });
    
    Route::group(['middleware' => ['api']], function(){
        Route::resource('products', 'API\ProductController');
    });
    
  2. エラーメッセージからルーティングに問題があるようです。

    おそらくですが、クラス指定の部分だと思います。
    最新のLaravelではコントローラーの指定を完全修飾名で行います。
    利用しているLaravelのバージョンを確認し、そのバージョンのドキュメントを確認してください。

    バージョン10.xのドキュメントより抜粋
    use App\Http\Controllers\PhotoController;
    
    Route::resource('photos', PhotoController::class);
    

    参考

  3. @hi_lili

    Questioner

    @blue32a
    Laravel のバージョンは9を利用しており、
    使用環境の記載を怠っておりました…。

    承知いたしました!
    一度ご教授頂いたものを元に修正致します!
    また、すぐに経過報告などをさせて頂ければと存じます🙇‍♂️

  4. @hi_lili

    Questioner

    @blue32a
    下記のように修正いたしました。

    api.php
    Route::group(['middleware' => ['api']], function(){
        Route::resource('products', [App\Http\Contoller\API\ProductController::class]);    
    });
    

    するとターミナルではこのようなエラーに変わりました。

     Array to string conversion
    

    文字列が...というようなものかと思いますが、
    修正点に検討がつかない状態です・・・。

  5. エラーメッセージから、文字列で定義すべきところを配列で定義してしまっているのではないかと推測します。
    [ ]で括って配列にしているのが原因だと思います。[ ]を取り除いてはいかがでしょうか?

    -   Route::resource('products', [App\Http\Contoller\API\ProductController::class]);    
    +   Route::resource('products', App\Http\Contoller\API\ProductController::class);    
    

    resourceの定義はgetpostとは異なります。

    Route::get('/user', [UserController::class, 'index']);
    Route::resource('photos', PhotoController::class);
    
  6. @hi_lili

    Questioner

    @blue32a 様
    ご教授ありがとうございます!

    私としてもmiddlewareへの理解度が乏しすすぎており、一度下記に修正いたしました。

    Route::get('product', [App\Http\Controllers\API\ProductController::class]);
    Route::post('product', [App\Http\Controllers\API\ProductController::class]);
    

    上記へ変更しターミナルで確認ができたルートは以下の通りとなります:bow_tone1:
    スクリーンショット 2024-03-13 15.51.11.png

    また、実際に飛ばそうとしているURLの「products/search」を手打ちすると
    以下のような文字が羅列しています(一部抜粋)
    スクリーンショット 2024-03-13 15.56.54.png

  7. 横レス失礼します。

    実際に飛ばそうとしているURLの「products/search」を手打ちすると
    以下のような文字が羅列しています(一部抜粋)

    「手打ち」とはどういう意味ですか? Laravel のサーバーは動いている状態で、ブラウザを立ち上げてそのアドレスバーに http から始まる search へのフルパスの url を打ち込んだら期待する JSON 文字列が応答として返されたと言ってますか?

    そうであれば、質問のコードにあった jQuery ajax のコードの url: "/products/search", // 検索処理を行うAPIのURL にそれと同じ url を設定したら同じように JSON 文字列が返されるはずなのですが、それでも 404 になりますか?

    別のスレッドで、フロントエンドとバックエンドを違う開発サーバーでホストしているのに、フロントエンドからフロントエンドのサーバーに要求を出して結果 404 となったという話がありましたけど、そのようなことはないでしょうね?

  8. @hi_lili

    Questioner

    @SurferOnWww

    「手打ち」とはどういう意味ですか? Laravel のサーバーは動いている状態で、ブラウザを立ち上げてそのアドレスバーに http から始まる search へのフルパスの url を打ち込んだら期待する JSON 文字列が応答として返されたと言ってますか?

    おっしゃる通りになります。
    ブラウザにてパスを打ち込むと上記画像の結果がブラウザにて返ってきてきている状態です。

    質問のコードにあった jQuery ajax のコードの url: "/products/search", // 検索処理を行うAPIのURL にそれと同じ url を設定したら同じように JSON 文字列が返されるはずなのですが、それでも 404 になりますか?

    おっしゃる通りだと思い、jsのurlを絶対パスに変更して試していたのですが、
    結果は変わらずに404になってしまいます・・・。
    urlの記載方法が間違っていたらすいません:bow_tone1:

    search.js
    url: "http://localhost:8888/product/public/products/search", 
    

    また、質問を頂きながら感じたのがJSONが返ってきているパスは上のURLであり、
    404で返ってきているURLが違っているのでルーティングに誤りがあって
    どうすれば・・・と考えているところになります:fearful:

    スクリーンショット 2024-02-27 15.47.18.png

    別のスレッドで、フロントエンドとバックエンドを違う開発サーバーでホストしているのに、フロントエンドからフロントエンドのサーバーに要求を出して結果 404 となったという話がありましたけど、そのようなことはないでしょうね?

    ここは私の知識が乏しく、回答がずれていたらすいません。
    作業は全て同じことPCで行っていたり、他の開発サーバーを交えたことがないため、
    そのようなことはないと思っております:fearful:

  9. http://localhost:8888/product/public/products/search をブラウザのアドレスバーに打ち込んで期待する JSON 文字列がブラウザに返ってくるのですよね。であれば、jQuery ajax の url の設定を全く同じにし、クエリ文字列が追加されないように data を削除 (またはコメントアウトコメント) して、すなわち以下のようにして、

        // AJAXリクエストを作成
        $.ajax({
          url: "http://localhost:8888/product/public/products/search",
          type: 'GET'
        }).done((response) => {
    

    要求をかければ同じ結果になる (サーバーから同じ JSON 文字列が返ってくる) はずです。試してみてください。

    Fiddler やブラウザの開発サーバーを使って要求・応答をキャプチャして、うまくいく場合とダメな場合を見比べてください。全く同じなのに一方が 404 となるとすると、上に書きましたが、ダメな方は違う開発サーバーに要求を出しているぐらいしか思い当たることはありません。

  10. 質問者さん、その後無言ですが、上のコメントの件は試してみましたか? レスをもらったら放置しないで、それに対するフィードバックを返してください。礼儀として。

    気になったことを追記しておきます。

    404で返ってきているURLが違っているのでルーティングに誤りがあってどうすれば・・・と考えているところになります

    ブラウザのアドレスバーに打ち込んでも、jQuery ajax の url に設定しても、サーバーに送信される url は http://localhost:8888/product/public/products/search で同じです。なので、ルーティングに誤りがあって 404 応答が返ってくるということはあり得ません。そのあたりの基本は勉強して理解しましょう。

    作業は全て同じことPCで行っていたり、他の開発サーバーを交えたことがないため、そのようなことはないと思っております

    同じ PC で開発サーバーを複数使うことはできます。

    上に書いた「別のスレッドで、フロントエンドとバックエンドを違う開発サーバーでホストしている」という例はどういうことだっかと言うと、フロントエンドの Vue を Node.js 開発サーバーでホストし、バックエンドの PHP ベースの Web API を PHP ビルトインサーバーでホストしていたというものです。

    で、Vue から Web API に要求を出さねばならないのに、Node.js 開発サーバーに要求を出していて、当然要求したリソースは Node.js 開発サーバーには無いので、404 応答が返ってきたという話です。

    それと似たような話は無いでしょうね?

  11. @hi_lili

    Questioner

    @SurferOnWww
    ご教授いただきありがとうござます!
    結論、絶対パスで記述を行った後に、再度キャッシュクリアを行うと
    無事解決することができました!

    無事解決することができましたので、この質問はクローズとさせていただきます:bow_tone1:

  12. 再度キャッシュクリアを行うと無事解決することができました!

    その「キャッシュ」というのはブラウザのキャッシュですか? だとすると何か変だと思うのですが。普通 Web アプリ(Web API を含む)の応答はキャッシュされないようにキャッシュコントロールされているはずなのですが・・・

  13. @hi_lili

    Questioner

    @SurferOnWww
    こちら、ターミナルにてキャッシュクリアを行っておりました。:bow_tone1:
    別のアンサーにてご教授いただいてものになります:grinning:

Comments

  1. @hi_lili

    Questioner

    @kamotetu
    返信が遅れてしまい、申し訳ありません:bow_tone1:
    ご指摘いただいたところを検証でどのように表示させるか分からず、すいません。

    どのように表示させればよろしいでしょうか・・・

  2. ブラウザ上で右クリックして、「検証」(chrome)とか「調査」(firefox)をクリックすると開発者モードが開くので、その状態でhtmlでどんな出力されているか見たらどうなっているかなと思いまして
    なんとなく

    localhost:8888/public/products/search?...
    

    のようなurlになってるかなーと思ったのです

    違ったらすいまそん

  3. @hi_lili

    Questioner

    @kamotetu
    違ってるなんて滅相もないです!
    確認いたします🙇‍♂️

    再度、経過報告をできればと存じます!

  4. @hi_lili

    Questioner

    @kamotetu
    確認方法が誤っていたらすいません・・・。

    http://localhost:8888/products/search?search=&company_id=&priceMin=&priceMax=&stockMin=&stockMax=
    

    検証結果はこのようになっておりました:bow_tone1:

  5. <form action="{{ route('products.index') }}" method="GET" class="row g-3" id="search-form">
    

    これのaction="???"ここがどう表示されているかなぁと思いまして
    で、これは目的のアクションにとんでいるのかなぁと思いまして

    ただ、別の方の回答でコントローラの作成方法が怪しいってありますので、私もそんな気がしました

    あと、試してほしいのは、api.phpじゃなくてweb.phpに同じように記載してどんな挙動をするかを確認してみるのもいいかもしれません

Your answer might help someone💌