ahoi

ahoi

Member Since 1 Year Ago

Experience Points
8,920
Total
Experience

1,080 experience to go until the next level!

In case you were wondering, you earn Laracasts experience when you:

  • Complete a lesson — 100pts
  • Create a forum thread — 50pts
  • Reply to a thread — 10pts
  • Leave a reply that is liked — 50pts
  • Receive a "Best Reply" award — 500pts
Lessons Completed
18
Lessons
Completed
Best Reply Awards
0
Best Reply
Awards
  • start your engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-in-session Created with Sketch.

    School In Session

    Earned when at least one Laracasts series has been fully completed.

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

    Earned once you receive your first "Best Reply" award on the Laracasts forum.

  • subscriber Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • evangelist Created with Sketch.

    Laracasts Evangelist

    Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.

  • chatty-cathy Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

    Earned once your "Best Reply" award count is 100 or more.

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

    Earned once your experience points ranks in the top 50 of all Laracasts users.

Level 2
8,920 XP
May
27
5 days ago
Activity icon

Started a new Conversation Chart: New Users Per Day

Hello everybody,

I would like to implement a js-charting lib to draw some charts for me.

In this case, I'd like to draw a chart of new users per day.

Well, most of those libs do accept an JSON containing a dataset. If I now start thinking, I could do something like:

  • Get all User
  • GroupBy created_at (day)
  • Count result per day

But in this case I would not match days without any new users, so there'd be a whole day missing. Is there any elegant way to "fill" days without a new user registration with a 0? Or is there even a package that does all this?

Thanks for your hints :-)

Activity icon

Started a new Conversation Check, Whether A Collection's Item Have The Same User_id

Hello everybody,

given the example that I got a collection Items, which contains n items like

- user
  - name
  - id

so for example my collection looks like:

Items:
 1: 
   id: 1
   user:
     name: "Foo"
     id: 15
 2: 
   id: 2
   user:
     name: "Bar"
     id: 12 

Now I'd like to check, whether user.id is unique in this collection or not.

What I am doing at the moment:

I pluck the user and then the id like this:

collect($value)->pluck('user.id')

Now I got an array of the user-id's.

Now I could perform the unique-method and count whether there are 1 or more results.

Maybe there is a more elegant way I did not think of?

Activity icon

Replied to Inverse Of BelongsToMany

Sorry, I gave both a "heart" and I wanted to give the "best Answer" and did not notice I can only give it once (which makes perfect sense - I did not get enough sleep today :-D ).

I really appreciate your answers and to show this, I'd be glad to donate a cup of coffee :-)

Activity icon

Started a new Conversation Inverse Of BelongsToMany

Hello everybody,

I hope the great Laracasts-community can help me with this one :-)

I got two Models:

  • Order
  • Invoice

Each Order can have many Invoices - and an Invoice can belong to many Orders.

So I can search for an Order and check: "Hey, which Invoices have been created for this Order?"

The other way round each Invoice can belong to multiple Orders, because maybe a customer ordered two products on the same day and so it would be great he'd only get one Invoice, which includes both orders.

So this is how I did this:

Invoice

public function orders()
{
    return $this->belongsToMany(Order::class);
}

Order

public function invoices()
{
    return $this->belongsToMany(Invoice::class, 'invoice_order');
}

This does work - but it does not seem right to change the table to the intermediate table invoice_order here. Do you have any thoughts on this? :-)

Thanks in advance for your thoughts :-)

May
17
2 weeks ago
Activity icon

Started a new Conversation One Model Containing Two Relationships Of The Same Type?

Hello everybody,

I'd like to solve this:

I got one model (Position). A position should contain a PositionProduct, which holds the ordered product with possible customizations and the OriginalPositionProduct, which basically contains the original product without any possible customizations. I need both to compare the actually ordered PositionProduct with it's original later.

Actually, this would not work:

/**
     * Get the related product.
     */
    public function product()
    {
        return $this->hasOne(PositionProduct::class);
    }
    
    public function originalproduct()
    {
        return $this->hasOne(PositionProduct::class);
    }

as the PositionProduct only contains the id of the position:

$table->bigInteger('position_id')->nullable();

As position_id is the same, product() and originalproduct() would return the same result.

So my question is:

How can I relate two instances of the same type but with different values to a model?

May
15
2 weeks ago
Activity icon

Replied to Cannot Add Foreign Key Constrain

Oh my god :facepalm:

I just had to switch the order of migrations in my second post. Thanks a lot!

Works fine!

Activity icon

Replied to Cannot Add Foreign Key Constrain

Hi and thanks a lot for your answer.

<?php

Schema::dropIfExists('product_attributes');
Schema::dropIfExists('product_product_attribute');


Schema::create('product_product_attribute', function ($table) {
    $table->bigIncrements('id');
    $table->bigInteger('product_id')->unsigned();
    $table->bigInteger('product_attribute_id')->unsigned();
    $table->boolean('custom')->nullable();
    $table->string('title')->nullable();
    $table->string('unit')->nullable();
    $table->string('type')->nullable();
    $table->text('value')->nullable();
    $table->float('price')->nullable();
    $table->bigInteger('position')->nullable();
    $table->timestamps();
    
    $table->foreign('product_id')->references('id')->on('position_products')->onDelete('cascade');
    $table->foreign('product_attribute_id')->references('id')->on('product_attributes')->onDelete('cascade');
});


Schema::create('product_attributes', function ($table) {
    $table->bigIncrements('id');
    $table->bigInteger('product_id')->unsigned();
    $table->string('title');
    $table->string('unit')->nullable();
    $table->string('type');
    $table->float('price')->nullable();
    $table->nestedSet();
    $table->timestamps();
    
    $table->foreign('product_id')->references('id')->on('position_products')->onDelete('cascade');
});

Throws the same error again

Activity icon

Started a new Conversation Cannot Add Foreign Key Constrain

Hello everybody,

I am trying to create a cascade on two migrations:


Schema::create('product_product_attribute',
    function ($table) {
        $table->bigIncrements('id');
        $table->bigInteger('product_id')->unsigned();
        $table->bigInteger('product_attribute_id')->unsigned();
        $table->boolean('custom')->nullable();
        $table->string('title')->nullable();
        $table->string('unit')->nullable();
        $table->string('type')->nullable();
        $table->text('value')->nullable();
        $table->float('price')->nullable();
        $table->bigInteger('position')->nullable();
        $table->foreign('product_id', 'pp_id')->references('id')
            ->on('products')->onDelete('cascade');
        $table->timestamps();
    });


Schema::create('product_attributes', function ($table) {
    $table->bigIncrements('id');
    $table->string('title');
    $table->string('unit')->nullable();
    $table->string('type');
    $table->float('price')->nullable();
    $table->nestedSet();
    $table->foreign('id')->references('product_attribute_id')
        ->on('products')->onDelete('cascade');
    $table->timestamps();
});

So what it is supposed to do:

If the Product that contains an attribute, both, the attribute and the pivot table for the attribute should be cascading.

This fails with:

Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alter table product_attributes add constraint product_attributes_id_foreign foreign key (id) references products (product_attribute_id) on delete cascade)'

Where did I do my mistake?

May
13
2 weeks ago
Activity icon

Awarded Best Reply on Prevent Form Submit Triggers Form Close

It's solved: I never ever would have thought this: The Password-Field had a <label>. Once I removed the for on that label, the problem was solved.

Activity icon

Replied to Prevent Form Submit Triggers Form Close

It's solved: I never ever would have thought this: The Password-Field had a <label>. Once I removed the for on that label, the problem was solved.

Activity icon

Started a new Conversation Save Additional Data On Model-creation

Hello everybody,

I got the following problem:

I need to save an item called InvoicePosition with a Product in my controller. Each Product can have ProductAttributes.

Although I am using the Product-Class I am not saving this product as relation in a database, but I use this class to "normalize" the input. Actually the data goes to a JSON-field in the database.

So:

InvoicePosition

- id (incr.)
- customer_id (integer)
- quantity (integer)
- product (JSON)

To save this position in my controller, I am doing this:

<?php
$product = new Product($position['product']);

$invoice->positions()->create([
    'quantity'           => $position['factor'],
    'product'            => $product,
]);

This works fine. To get the Product later, I am using this accessor:

public function getProductAttribute($value)
{
    $value                     = (array)json_decode($value);
    $product                = new Product($value);
    $product                = $product->with('productAttributes')
        ->first();
    return $product;
}

This is working fine, too - I am getting an instance of App\Product:

=> App\Product {#1228
     id: 1,
     price: 1.99,
     productAttributes: [...]

Now I got a problem: I need to save additional information in the JSON-field of the product. In my case, I need to duplicate the productAttributes of each Product.

So I thought about something like this in the controller:

<?php
$product = new Product($position['product']);

$product->productAttributes()->map(function($attribute){
  $attribute->original = $attribute;
  return $attribute;
});

$invoice->positions()->create([
    'quantity'           => $position['factor'],
    'product'            => $product,
]);

But now my accessor does not reflect these changes, as App\Product includes App\ProductAttribute but not the additional field.

May
12
2 weeks ago
Activity icon

Replied to Cast JSON To A New Model (including It's Own Casts)

Thanks a lot for all your comments :-)

I can not use relations, because the product that belongs to the Position should be available "as it is" even if the Product itself does no longer exist.

Activity icon

Started a new Conversation Cast JSON To A New Model (including It's Own Casts)

I would like to solve the following thing: I got a model including a JSON-object. This JSON is representing a Product, but it should not be synced with the actual Product as it should be "alive" even if the product is being deleted. Additionally the JSON may contain custom information about the Product.

If I call my model Product, I'd like to make sure that specific information within this JSON are casted just like the original Product. So product->price should always be a decimal:2. Even if the JSON may contain a string with the value 1.99.

As I am not able to "deep cast" the JSON, I am looking at the Castables (https://laravel.com/docs/7.x/eloquent-mutators#custom-casts)

Would this be the way to go? And: Can I somehow import the casts of the Product model there?

This is the model called Position:

class Position extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable
        = [
            'product'
        ];
    
    /**
     * @var array
     */
    protected $casts
        = [
            'product'          => 'object'
        ];
    
    /**
     * Get the owning positionable model.
     */
    public function positionable()
    {
        return $this->morphTo();
    }

So my question is:

Is it possible to cast the product JSON in my Position model just like the Product model without creating a relation?

May
11
3 weeks ago
Activity icon

Started a new Conversation Conditionally Loaded Data In API Resource: How To "pass" A Condition?

Hello everybody,

I got a little issue to solve. In my app I am handling with a lot of Models and each model does have something like:

  • ModelResource
  • ModelResourceCollection
  • ModelResourceOverview
  • ModelResourceOverviewCollection

The reason is: Sometimes I don't need all the information that are visible if I am using the ModelResource - in this case I am calling the ModelResourceOverview.

Example

PostResource

- title
- tags
- content
- author

Example

PostOverviewResource

- title
- author

As I have a lot of Models I have a huge number of ApiResource-Classes and I want to get rid of that.

I thought about using $this->when in the Resource and pass something like "full" or "overview" to the request in the Controller.

Example

$data
    = new PostResource($this->post);

So my question is: Is it the best way to add this to the request or is there a handier/nicer way to handle this?

May
10
3 weeks ago
Activity icon

Started a new Conversation Guzzle: Float Changes To String

Hello everybody,

I am trying to post some data using Guzzle like this:

<?php
$client  = $this->client;
$this->payload = ['product' => ['title' => 'footer', 'price' => 12.99]];

$options = [];

$options['headers'] = [
    'Accept'        => 'application/json',
];

if ($this->method === 'POST' && $this->payload) {
    $options['json'] = $this->payload;
}

$response = $client->request($this->method,
    'https://host.local', $options);
}

Well - this works fine, but unfortunately the float 12.99 is transmitted as string to host.local.

host.local runs a Laravel-API and unfortunately I really need to accept a full JSON object containing float instead of string.

 public function store(Request $request)
    {
        /*Validate request*/
        $request->validate([
            'data.product'                  => 'nullable|array',
            'data.product.title'            => 'required|string',
            'data.product.price'            => 'required|numeric',
        ]);
 
        $offer = Offer::create([
            'product' => $request->input('data.product'),
        ]);
    }

This is saving the JSON object with a string "12.99".

Anything I can do to:

  • Transmit the value as float
  • Use the Controller to handle the given value to transform them into the correct type?
May
06
3 weeks ago
Activity icon

Replied to Wierdness On Live Server

Hi. No, at least OPCache is bundled to PHP (i think starting with 7.0?) OPCache can cache PHP-Objects and sometimes it can behave a little weird.

If both of your setups are exactly the same and also the database is the same and the $user is passed to the view without any conditions, this is a reason I can imagine.

Actually I don't think that's the reason, but it's worth a shot.

Check php -v and phpinfo(); and if it's enabled, disable it by adding opcache.enable=0 to your php.ini and restart nginx/apache/php-fpm.

Could you also please add the information on how the $user is being passed to the view? It should be in your controller :-)

Activity icon

Replied to Wierdness On Live Server

Just to be sure: Is any cache enabled like OPCache, xcache, apcu?

Apr
30
1 month ago
Activity icon

Replied to Prevent Form Submit Triggers Form Close

A little update:

I figured out that this happens as long as the password-field contains type='password':

<input
            type="password"
            :class="{'appearance-none block w-full input shadow-lg input-white': true,  'input-error': errors[0]}"
            v-bind="$attrs"
            v-model="innerValue"
        >
Activity icon

Started a new Conversation Prevent Form Submit Triggers Form Close

I got a problem: I have a little user menu that is shown to log in. To handle outside clicks to close the menu again, I implemented this little helper:

<on-click-outside :do="handleClickOutside">
	<Login/>
</on-click-outside>

this triggers

 handleClickOutside () {
 if (this.open) {
  this.open = false;
  }
},

Alright. This is the component:

<script>
    export default {
        props: ["do"],
        mounted () {
            const listener = e => {
                if (e.target !== this.$el && !this.$el.contains (e.target)) {
                    this.do ();
                }
            };
            document.addEventListener ("click", listener);
            this.$once ("hook:destroyed", () => {
                document.removeEventListener ("click", listener);
            });
        },
        render () {
            return this.$slots.default[0];
        }
    };
</script>

This works fine, but if I log in this way:

<ValidationObserver ref="loginform" v-if="!authenticated">
<form v-on:submit.prevent>
...
<button
    :class="{'btn shadow-lg sm:text-base': true, 'opacity-50 cursor-not-allowed': $wait.is('user.login')}"
    @click.prevent="login">
    Login
</button>

</form>
</ValidationObserver>

The handleClickOutside method is being triggered and the login-form is not visible. Any idea why this is happening?

Apr
27
1 month ago
Activity icon

Started a new Conversation Array Is Returned As An Object Using Resource Via Axios

Hello everybody,

something strange is going on.

I got an array like this:

=> [
     "optionalinformation" => [
       "domain" => [
         "type" => "string",
       ],
     ],
   ]

This array is used by a resource and if I use tinker to check this resource like this:

$result = App\Http\Resources\ProductResource::make(Product::find(2));

is_array($result->optionalinformation);

In this case the result is true: This is an array.

But if axios fetches the result, I am getting this:

"optionalinformation": {
      "domain": {
        "type": "string"
      },
     

It's no longer an array but an object. Any ideas why this is happening?

This is my api-resource:

 /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id'                      => $this->id,
            'title'                   => $this->title,
            'optionalinformation'     => $this->optionalinformation,
        ];
    }
Apr
24
1 month ago
Activity icon

Started a new Conversation Modify Result In A BelongsTo-relation

Hello everybody,

I just wonder how I can solve this:

I got a Product with a belongsTo relation.

/**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function supplier()
    {
        return $this->belongsTo(Supplier::class);
    }

This is the result of a related supplier:

{
  "id": 1,
  "requirements": {
    "product": {
      "deliveryservice": {
        "type": "string",
        "title": "Delivery-Service"
      }
    }
  }
}

Now I would like to solve a value for the supplier's requirements.

This is the input:

<b-form-group :key="index" :label="requirement.title" v-for="(requirement, index) in product.supplier.requires.product">
	<component
			:id="`requirement-${index}-value`"
			:is="requirement.type"
			:name="`requirement-${index}-value`"
			:required="true"
			v-model="requirement.value">
	</component>
</b-form-group>

The result is being stored in the Product table:

/*Create product*/
$product = Product::create([
    'product_supplier_value' => $request->input('data.supplier.requires.product'),
]);

Now if I load a product, I can see this:

{
  "id": 2,
  "deleted_at": null,
  "created_at": "2020-04-22 09:30:34",
  "updated_at": "2020-04-24 10:16:02",
  "product_supplier_id": 1,
  "product_supplier_value": {
    "product": {
      "type": "string",
      "title": "Delivery-Service",
      "value": "DHL"
    }
  }
}

Okay. So now if I load the page, the value is not shown because it's not available in product.supplier.requires.product.

So my question: How can I merge the value stored in the JSON-row product_supplier_value with the belongsTo-relation?

At the moment I am doing some kind of a crappy thing by modifying the API-resource of Product mapping over the result and adding the finding. But that's not the best way I guess.

Apr
23
1 month ago
Activity icon

Replied to One To Many With Pivot Not Possible. How To Redesign Relationships?

I updated my question with a better "real-world"-example.

Activity icon

Replied to One To Many With Pivot Not Possible. How To Redesign Relationships?

Hi @psylogic ,

I am not sure if I understand your point. This article handles a recursive relationship (which is cool btw :-) ), but there's no recursion in my case.

I just need one Item to have one Child with a pivot information :-)

If I got you wrong, please tell me :-)

Apr
22
1 month ago
Activity icon

Started a new Conversation One To Many With Pivot Not Possible. How To Redesign Relationships?

I would like to do the following:

Product can have one Supplier. The Supplier contains some data like a name. Additionally there is a pivot-value that should be stored for Supplier that is assigned to Product (e.g. a delivery_service-string).

Example:

  • Product: A yummy Banana
  • Supplier: Banana Inc

-> If the Product "A yummy Banana" is supplied by Supplier it should be delivered by DHL. The important thing: You can not add DHL as a field to Supplier, as each Product to Supplier-relation should have it's own delivery-service-field.

As there can be many Products but each Product can only have one Supplier I thought about something like this:

Product

Schema:

- id
- supplier_id

Relation

public function supplier()
{
    return $this->belongsTo(Supplier::class);
}

Supplier

Schema:

- id
- name

Relation

public function products()
{
    return $this->hasMany(Product::class);
}

This works, but unfortunately I can not store pivot data in the supplier() relation.

At the moment I could imagine to store the value not in a pivot table but in a new row in the Product's schema. But I don't think this is the best way.

Any suggestions? :-)

Apr
17
1 month ago
Activity icon

Replied to Database-less User Authentication: Is My Solution Okay?

Hi @bugsysha and thanks a lot for your response.

That's my point: Testing is very difficult using this. But I did not figure out another way to use a "remote login" without oauth/authorize.

In my message I left out the ApiClient which executes the POST-request to the user-provider.

So I assume I will have to overthink the static methods in here, which I adopted from the linked example. What else do you mean with "breaking quite a few programming principles"?

Activity icon

Started a new Conversation Database-less User Authentication: Is My Solution Okay?

Hello everybody,

I needed to authenticate my users not using the database of my Laravel-app but using the database of another Laravel-app, which provides OAUTH2 using passport.

The important thing: The OAUTH2-server is not accessible for the users - it's only accessible for the public Laravel app using a private network.

So unfortunately I cannot use Socialite or something like this for my public Laravel app.

So this is my solution based on https://gist.github.com/eusonlito/8b5389db1d390c17aba123645fd99ea1 and I would like to hear your comments on this implementation and possible security risks:

app/Http/Controllers/Auth/LoginController.php

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Services\User\Auth;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    
    use AuthenticatesUsers;

   [...]

    public function login(Request $request)
    {
        $user = Auth::byCredentials($request->input('email'),
            $request->input('password'));

        return $user;
    }
}

app/Providers/AuthServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth as BaseAuth;
use App\Services\Auth\UserProvider;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies
        = [// 'App\Model' => 'App\Policies\ModelPolicy',
        ];
    
    public function boot()
    {
        $this->registerPolicies();
        $this->provider();
    }
    
    protected function provider(): void
    {
        BaseAuth::provider('custom', static function ():
        UserProvider {
            return new UserProvider();
        });
    }
}

app/Repositories/User.php

<?php

namespace App\Repositories;

use App\Http\Services\ApiClient;

class User extends RepositoryInterface
{
    public static function detail(): \App\User
    {
        $client = new ApiClient();
        $client->setMethod('GET');
        $client->setRoute('/api/user');
        $client->setToken(session('token'));
        
        $response      = $client->execute();
        $user          = json_decode($response, true)['data'];
        $user['token'] = $client->getToken();
        
        return new \App\User($user);
    }
}

app/Services/Auth/UserProvider.php

<?php

namespace App\Services\Auth;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as AuthProvider;
use App\Repositories;

class UserProvider implements AuthProvider
{
    
    public function retrieveById($identifier): Authenticatable
    {
        return Repositories\User::detail();
    }

    public function retrieveByToken($identifier, $token)
    {
    }
    
    public function updateRememberToken(Authenticatable $user, $token)
    {
    }
    
    public function retrieveByCredentials(array $credentials)
    {
    }
    
    public function validateCredentials(Authenticatable $user, array $credentials)
    {
    }
}

app/Services/User/Auth.php

<?php

namespace App\Services\User;

use App\Http\Services\ApiClient;
use App\User;
use Illuminate\Support\Facades\Auth as BaseAuth;


class Auth
{

    public static function byCredentials(
        string $user,
        string $password
    ) {
        
        $client = new ApiClient();
        $client->setMethod('GET');
        $client->setRoute('/api/user');
        $client->setCredentials([
            'username' => $user,
            'password' => $password,
        ]);
        
        try {
            $response = $client->execute();
        } catch (\Exception $e) {
            $validation = response()->json([
                'message' => json_decode($e->getMessage())->message,
            ], $e->getCode());
            
            $loginresponse = $client->validateResponse($validation);
            
            if ($loginresponse !== true) {
                return $loginresponse;
            }
            
        }
        
        $user          = json_decode($response, true)['data'];
        $user['token'] = $client->getToken();
        
        return static::auth(new User($user));
    }
    
    protected static function auth(User $user): User
    {
        static::session($user);
        static::bind($user);
        static::login($user);
        
        return $user;
    }
    
    protected static function session(User $user): void
    {
        session(['token' => $user->token]);
    }
    
    protected static function bind(User $user): void
    {
        app()->bind('user', static function () use ($user): User {
            return $user;
        });
        
        app()->bind('token', static function (): string {
            return session('token');
        });
    }
    
    protected static function login(User $user): void
    {
        BaseAuth::login($user, true);
    }
}

app/User.php

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;

class User extends Models\ModelInterface implements AuthenticatableContract
{
    use Authenticatable;
    
    public function getAuthIdentifierName(): string
    {
        return 'token';
    }

    public function getAuthIdentifier(): string
    {
        return $this->token;
    }
    
}
Apr
16
1 month ago
Activity icon

Replied to Trying To Get Property 'resource' Of Non-object In PaginatedResourceResponse.php After Laravel-update

Hi @snapey

I saw this... But I did not find a good solution for this.

At the moment I can do something like

$this->collectResource($this->collection)
                ->transform(...)

This does work.

Activity icon

Started a new Conversation Trying To Get Property 'resource' Of Non-object In PaginatedResourceResponse.php After Laravel-update

Hello everybody,

actually I am getting this error after updating Laravel from v7.5.1 to v7.6.2

"message": "Trying to get property 'resource' of non-object",
    "exception": "ErrorException",
    "file": "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Http/Resources/Json/PaginatedResourceResponse.php",
    "line": 29,
    "trace": [
        {
            "file": "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Http/Resources/Json/PaginatedResourceResponse.php",
            "line": 29,
            "function": "handleError",
            "class": "Illuminate\Foundation\Bootstrap\HandleExceptions",
            "type": "->"
        },
        {
            "function": "Illuminate\Http\Resources\Json\{closure}",
            "class": "Illuminate\Http\Resources\Json\PaginatedResourceResponse",
            "type": "->"
        },
        {
            "file": "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Support/Collection.php",
            "line": 638,
            "function": "array_map"
        },
        {
            "file": "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php",
            "line": 23,
            "function": "map",
            "class": "Illuminate\Support\Collection",
            "type": "->"
        },

This is what causes the error:

$products = Product::findMany($array)->paginate(10);
//dump($products);

/*Create ResourceCollection*/
return new ProductResourceCollection($products);

The dump returns this:

------------ ----------------------------------------------- 
  date         Thu, 16 Apr 2020 08:53:53 +0000                
  controller   "ProductApiController"                         
  source       ProductApiController.php on line 56            
  file         app/Http/Controllers/ProductApiController.php  
 ------------ ----------------------------------------------- 

Illuminate\Pagination\LengthAwarePaginator^ {#300
  #total: 1
  #lastPage: 1
  #items: Illuminate\Database\Eloquent\Collection^ {#319
    #items: array:1 [
      0 => App\Product^ {#400
        #casts: array:9 [
          "category" => "object"
        ]
        -client: App\Http\Services\ApiClient^ {#377
          #method: "GET"
          #route: "/api/product/index?length=-1&response=full"
          #payload: null
          #credentials: array:2 [
               ...
          ]
          #token: null
          -client: GuzzleHttp\Client^ {#399
            -config: array:8 [
              "verify" => false
              "handler" => GuzzleHttp\HandlerStack^ {#398
                -handler: Closure(RequestInterface $request, array $options)^ {#391
                  class: "GuzzleHttp\Handler\Proxy"
                  use: {
                    $default: Closure(RequestInterface $request, array $options)^ {#393 …}
                    $streaming: GuzzleHttp\Handler\StreamHandler {#392 …}
                  }
                  file: "./vendor/guzzlehttp/guzzle/src/Handler/Proxy.php"
                  line: "49 to 53"
                }
                -stack: array:4 [
                  0 => array:2 [
                    0 => Closure(callable $handler)^ {#390
                      class: "GuzzleHttp\Middleware"
                      file: "./vendor/guzzlehttp/guzzle/src/Middleware.php"
                      line: "54 to 69"
                    }
                    1 => "http_errors"
                  ]
                  1 => array:2 [
                    0 => Closure(callable $handler)^ {#389
                      class: "GuzzleHttp\Middleware"
                      file: "./vendor/guzzlehttp/guzzle/src/Middleware.php"
                      line: "148 to 150"
                    }
                    1 => "allow_redirects"
                  ]
                  2 => array:2 [
                    0 => Closure(callable $handler)^ {#388
                      class: "GuzzleHttp\Middleware"
                      file: "./vendor/guzzlehttp/guzzle/src/Middleware.php"
                      line: "26 to 43"
                    }
                    1 => "cookies"
                  ]
                  3 => array:2 [
                    0 => Closure(callable $handler)^ {#387
                      class: "GuzzleHttp\Middleware"
                      file: "./vendor/guzzlehttp/guzzle/src/Middleware.php"
                      line: "216 to 218"
                    }
                    1 => "prepare_body"
                  ]
                ]
                -cached: null
              }
              "allow_redirects" => array:5 [
                "max" => 5
                "protocols" => array:2 [
                  0 => "http"
                  1 => "https"
                ]
                "strict" => false
                "referer" => false
                "track_redirects" => false
              ]
              "http_errors" => true
              "decode_content" => true
              "cookies" => false
              "idn_conversion" => true
              "headers" => array:1 [
                "User-Agent" => "GuzzleHttp/6.5.1 curl/7.58.0 PHP/7.4.1"
              ]
            ]
          }
        }
        #connection: null
        #table: "products"
        #primaryKey: "id"
        #keyType: "int"
        +incrementing: true
        #with: []
        #withCount: []
        #perPage: 15
        +exists: true
        +wasRecentlyCreated: false
        #attributes: array:21 [
          "id" => "1"
          "title" => "Test"
          "category" => "{"id":1,"title":"Test","slug":null}"
        ]
        #original: array:21 [
          "id" => "1"
          "title" => "Test"
          "category" => "{"id":1,"title":"Test","slug":null}"
        ]
        #changes: []
        #classCastCache: []
        #dates: []
        #dateFormat: null
        #appends: []
        #dispatchesEvents: []
        #observables: []
        #relations: []
        #touches: []
        +timestamps: true
        #hidden: []
        #visible: []
        #fillable: []
        #guarded: array:1 [
          0 => "*"
        ]
      }
    ]
  }
  #perPage: 10
  #currentPage: 1
  #path: "http://example.org/api/product/pluck/1,2,3"
  #query: []
  #fragment: null
  #pageName: "page"
  +onEachSide: 3
  #options: array:2 [
    "path" => "http://example.org/api/product/pluck/1,2,3"
    "pageName" => "page"
  ]
}

This is the resource:

public function toArray($request)
{
    return [
        'current_page'   => $this->resource->currentPage(),
        'first_page_url' => $this->resource->url(1),
        'from'           => $this->resource->firstItem(),
        'last_page'      => $this->resource->lastPage(),
        'last_page_url'  => $this->resource->url($this->lastPage()),
        'next_page_url'  => $this->resource->nextPageUrl(),
        'per_page'       => $this->resource->perPage(),
        'prev_page_url'  => $this->resource->previousPageUrl(),
        'to'             => $this->resource->lastItem(),
        'total'          => $this->resource->total(),
        'data'           => $this->collection->transform(function ($product
        ) {
            return [
                'id'                => $product->id,
                'title'             => $product->title,
                'category'          => $product->category,
            ];
        }),
    ];
}

If I comment 'data' => $this->collection->transform(function ($product) { the error does not appear... But... Well, then the app does not work ;-)

Activity icon

Replied to [vuex] Module Namespace Not Found In MapActions()

I don't get your point @shoemoney

Vuex has no dependencies to Laravel..?

Activity icon

Replied to [vuex] Module Namespace Not Found In MapActions()

Hi Prashant :-)

I am not sure if it's the same problem if you have a look at the error messages.

I'm not sure where your error comes from - can you post the stacktrace?

Apr
15
1 month ago
Activity icon

Started a new Conversation Use E-Mail And Password To Authenticate On OAUTH2-provider

Hello everybody,

I got a laravel-based app, which provides Laravel/Passport to it's frontend SPA.

Now I'd like to use this OAUTH2-server in my second Laravel app to provide login-functionality to the users.

List of applications

  • Laravel-Provider: OAUTH2-provider based on Laravel/Passport
  • Laravel-Client: Laravel app

Both services can access each other using an internal network. The Laravel-Provider is not public so it's not exposed to the "www".

My goal

I would like to allow users on Laravel-Client to login to Laravel-Client using the credentials of the user stored in the Laravel-Provider app.

My attempts

At first I was checking out Laravel/Socialite to connect to the Laravel-Provider. But in this case the user who visited Laravel-Client is being redirected to Laravel-Provider's oath/authorize URL and needs to provide it's credentials there. Additionally the user should not be able to access Laravel-Provider directly.

Now I am thinking about some kind of a custom User provider on Laravel-Client which is not storing user-data in the database (or maybe it is, but no password- or login-information), but I don't see good resources on the internet to get examples for that.

I am already able to fetch information using some kind of "superuser" over the API provided by Laravel-Provider:

This fetches a token:

return $client->request('POST',
    'https://laravel-provider.test/oauth/token', [
        'headers'     => [
            'cache-control' => 'no-cache',
            'Content-Type'  => 'application/x-www-form-urlencoded',
        ],
        'form_params' => [
            'client_id'     => config('accounting.client_id'),
            'client_secret' => config('accounting.client_secret'),
            'grant_type'    => 'password',
            'username'      => config('accounting.user'),
            'password'      => config('accounting.password'),
        ],
    ]);

This fetches information:

<?php
$client   = $this->client;
$response = $client->request('GET',
    'https://laravel-provider.test/test', [
        'headers' => [
            'Accept'        => 'application/json',
            'Authorization' => 'Bearer '.$this->getAccessToken(),
        ],
    ]);

My questions

  • Is it even possible to log in my user on Laravel-Client using the credentials stored on Laravel-Provider?
  • Do you know any resources that handle this request?
Apr
14
1 month ago
Activity icon

Replied to Passport: Login From External Website

Hi,

I checked out your blog and I am trying to get it working ☺️ Unfortunately I am struggling with my ID-Provider (https://laracasts.com/discuss/channels/laravel/use-laravelpassport-as-oauth2-provider-and-log-in-a-user-using-socialite)

If you have a minute I really would appreciate your help on that one ☺️

Activity icon

Started a new Conversation Use Laravel/Passport As OAUTH2-provider And Log In A User Using Socialite

I would like to login an user that exists on one Laravel-app on another site.

On Laravel-Prodiver I created a new OAuth-client:

mysql> select * from oauth_clients;
+----+---------+-----------------------------------+------------------------------------------+-------------------------------------------+------------------------+-----------------+---------+---------------------+---------------------+
| id | user_id | name                              | secret                                   | redirect                                  | personal_access_client | password_client | revoked | created_at          | updated_at          |
+----+---------+-----------------------------------+------------------------------------------+-------------------------------------------+------------------------+-----------------+---------+---------------------+---------------------+
|  1 |    NULL | Accounting Personal Access Client | 1231231231231231231231231231231231231231 | http://localhost                          |                      1 |               0 |       0 | 2020-02-09 14:24:32 | 2020-02-09 14:24:32 |
|  2 |    NULL | Accounting Password Grant Client  | 1231231231231231231231231231231231231231 | http://laravel-client.test/oauth/callback |                      0 |               1 |       0 | 2020-02-09 14:24:32 | 2020-02-09 14:24:32 |
|  3 |    NULL | Password Grant Client             | 1231231231231231231231231231231231231231 | http://localhost                          |                      0 |               1 |       0 | 2020-04-14 12:30:21 | 2020-04-14 12:30:21 |
+----+---------+-----------------------------------+------------------------------------------+-------------------------------------------+------------------------+-----------------+---------+---------------------+---------------------+

On Laravel-prodiver I added Passport::routes(); to AuthServiceProvider.

|        | GET|HEAD | api/user/{id?}                          | api.user.view                        | App\Http\Controllers\[email protected]                               | api,auth:api |
|        | GET|HEAD | oauth/authorize                         | passport.authorizations.authorize    | Laravel\Passport\Http\Controllers\[email protected]       | web,auth     |
|        | POST     | oauth/authorize                         | passport.authorizations.approve      | Laravel\Passport\Http\Controllers\[email protected]  | web,auth     |
|        | DELETE   | oauth/authorize                         | passport.authorizations.deny         | Laravel\Passport\Http\Controllers\[email protected]        | web,auth     |
|        | POST     | oauth/clients                           | passport.clients.store               | Laravel\Passport\Http\Controllers\[email protected]                  | web,auth     |
|        | GET|HEAD | oauth/clients                           | passport.clients.index               | Laravel\Passport\Http\Controllers\[email protected]                | web,auth     |
|        | DELETE   | oauth/clients/{client_id}               | passport.clients.destroy             | Laravel\Passport\Http\Controllers\[email protected]                | web,auth     |
|        | PUT      | oauth/clients/{client_id}               | passport.clients.update              | Laravel\Passport\Http\Controllers\[email protected]                 | web,auth     |
|        | POST     | oauth/personal-access-tokens            | passport.personal.tokens.store       | Laravel\Passport\Http\Controllers\[email protected]     | web,auth     |
|        | GET|HEAD | oauth/personal-access-tokens            | passport.personal.tokens.index       | Laravel\Passport\Http\Controllers\[email protected]   | web,auth     |
|        | DELETE   | oauth/personal-access-tokens/{token_id} | passport.personal.tokens.destroy     | Laravel\Passport\Http\Controllers\[email protected]   | web,auth     |
|        | GET|HEAD | oauth/scopes                            | passport.scopes.index                | Laravel\Passport\Http\Controllers\[email protected]                     | web,auth     |
|        | POST     | oauth/token                             |                                      | Laravel\Passport\Http\Controllers\[email protected]        | throttle     |
|        | POST     | oauth/token/refresh                     | passport.token.refresh               | Laravel\Passport\Http\Controllers\[email protected]        | web,auth     |
|        | GET|HEAD | oauth/tokens                            | passport.tokens.index                | Laravel\Passport\Http\Controllers\[email protected] | web,auth     |
|        | DELETE   | oauth/tokens/{token_id}                 | passport.tokens.destroy              | Laravel\Passport\Http\Controllers\[email protected] | web,auth     |
|        | GET|HEAD | {any}                                   |                                      | App\Http\Controllers\ApplicationController                                | web          |

So if I now open http://laravel-provider.test/oauth/authorize I am being redirected to my home page.

If I add a route without the auth middleware like this:

Route::get('oauth/authorize', '\Laravel\Passport\Http\Controllers\[email protected]');
Route::get('/{any}', 'ApplicationController')->where('any', '.*');

I can access the route and I am able to create a Socialite-redirection like this:

return Socialite::with('laravelpassport')->setConfig($config)
        ->redirect();

In this case I am getting this error:

Error
Call to a member function getKey() on null
http://laravel-provider.test/oauth/authorize?client_id=2&redirect_uri=http%3A%2F%laravel-client.test%2Foauth%2Fcallback&response_type=code&scope=&state=4TUCV7HLfSROjoF1AShmseQZybRB8XbuKX9vM1mZ

This is the part where the error is triggered:

**
     * Find a valid token for the given user and client.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $user
     * @param  \Laravel\Passport\Client  $client
     * @return \Laravel\Passport\Token|null
     */
    public function findValidToken($user, $client)
    {
        return $client->tokens()
                      ->whereUserId($user->getKey())
                      ->where('revoked', 0)
                      ->where('expires_at', '>', Carbon::now())
                      ->latest('expires_at')
                      ->first();
    }

My questions are:

  • What am I doing wrong here? Why is my route not callable? Am I right assuming that the auth-middleware does not work here?
  • How can I "open" Passport to allow users log in on third-party-websites?
  • Why is the auth middleware default here and how can I assure that people can log in using Socialite?
Activity icon

Replied to Passport As OAUTH2-server -> Authenticate Using Socialite

Alright,

worked a little more on that.

I still don't understand why the routes that are provided to log in is protected by web, auth:

|        | GET|HEAD | oauth/authorize                         | passport.authorizations.authorize    | Laravel\Passport\Http\Controllers\[email protected]       | web,auth     |
|        | POST     | oauth/authorize                         | passport.authorizations.approve      | Laravel\Passport\Http\Controllers\[email protected]  | web,auth     |

So if I remove Passport::routes(); from AuthServiceProvider and add this to my routes:

Route::get('oauth/authorize', '\Laravel\Passport\Http\Controllers\[email protected]');
Route::delete('oauth/authorize', '\Laravel\Passport\Http\Controllers\[email protected]')->middleware('auth:api');

I can access the route using:

return Socialite::with('laravelpassport')->setConfig($config)->redirect();

but on the app I am getting this error:

Call to a member function getKey() on null
http://test.test/oauth/authorize?client_id=2&redirect_uri=http%3A%2F%example.test%2Foauth%2Fcallback&response_type=code&scope=&state=4TUCV7HMLfSROjoF1AShseQZybRB8XbuKX9vM1mZ

This happens on Laravel\Passport\TokenRepository::findValidToken

 /**
     * Find a valid token for the given user and client.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $user
     * @param  \Laravel\Passport\Client  $client
     * @return \Laravel\Passport\Token|null
     */
    public function findValidToken($user, $client)
    {
        return $client->tokens()
                      ->whereUserId($user->getKey())
                      ->where('revoked', 0)
                      ->where('expires_at', '>', Carbon::now())
                      ->latest('expires_at')
                      ->first();
    }

Alright, so $user is null here.

So my question is: How can I solve this issue?

Activity icon

Replied to Passport As OAUTH2-server -> Authenticate Using Socialite

I got one more thing:

If I - for testing - add this to the web routes:

Route::get('/oauth/authorize', '\Laravel\Passport\Http\Controllers\[email protected]');
Route::get('/', 'ApplicationController')->where('any', '.*');

I can access the authorization view.

So I guess my Passport-routes are not registered correctly?

I don't really understand this, because I added:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
use App\User;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies
        = [// 'App\Model' => 'App\Policies\ModelPolicy',
        ];
    
    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
        
        /*add super-admin role*/
        Gate::before(function ($user, $ability) {
            if ($user->hasRole('super-admin')) {
                return true;
            }
        });
        
        /*Dashboard*/
        Gate::define('view-dashboard', function ($user) {
            return $user->can('view dashboard')
                && $user->status === User::STATUS_ACTIVE;
        });
        
    }
}        
Activity icon

Started a new Conversation Passport As OAUTH2-server -> Authenticate Using Socialite

Hello everybody,

I would like to use Laravel Passport as OAUTH2-provider to log in a user on another website using Socialite.

To get this working I am using https://github.com/diadal/vue-social-auth .

But now I'm confused: I would need to specify some kind of "HTTP Endpoint" to show a login or "yes, log in using my account" popup, right? But where can I find this endpoint?

If I perform php artisan route:list I am getting this result:

|        | GET|HEAD | oauth/authorize                         | passport.authorizations.authorize    | Laravel\Passport\Http\Controllers\[email protected]       | web,auth     |
|        | POST     | oauth/authorize                         | passport.authorizations.approve      | Laravel\Passport\Http\Controllers\[email protected]  | web,auth     |
|        | DELETE   | oauth/authorize                         | passport.authorizations.deny         | Laravel\Passport\Http\Controllers\[email protected]        | web,auth     |
|        | POST     | oauth/clients                           | passport.clients.store               | Laravel\Passport\Http\Controllers\[email protected]                  | web,auth     |
|        | GET|HEAD | oauth/clients                           | passport.clients.index               | Laravel\Passport\Http\Controllers\[email protected]                | web,auth     |
|        | DELETE   | oauth/clients/{client_id}               | passport.clients.destroy             | Laravel\Passport\Http\Controllers\[email protected]                | web,auth     |
|        | PUT      | oauth/clients/{client_id}               | passport.clients.update              | Laravel\Passport\Http\Controllers\[email protected]                 | web,auth     |
|        | POST     | oauth/personal-access-tokens            | passport.personal.tokens.store       | Laravel\Passport\Http\Controllers\[email protected]     | web,auth     |
|        | GET|HEAD | oauth/personal-access-tokens            | passport.personal.tokens.index       | Laravel\Passport\Http\Controllers\[email protected]   | web,auth     |
|        | DELETE   | oauth/personal-access-tokens/{token_id} | passport.personal.tokens.destroy     | Laravel\Passport\Http\Controllers\[email protected]   | web,auth     |
|        | GET|HEAD | oauth/scopes                            | passport.scopes.index                | Laravel\Passport\Http\Controllers\[email protected]                     | web,auth     |
|        | POST     | oauth/token                             |                                      | Laravel\Passport\Http\Controllers\[email protected]        | throttle     |
|        | POST     | oauth/token/refresh                     | passport.token.refresh               | Laravel\Passport\Http\Controllers\[email protected]        | web,auth     |
|        | GET|HEAD | oauth/tokens                            | passport.tokens.index                | Laravel\Passport\Http\Controllers\[email protected] | web,auth     |
|        | DELETE   | oauth/tokens/{token_id}                 | passport.tokens.destroy              | Laravel\Passport\Http\Controllers\[email protected] | web,auth     |
|        | GET|HEAD | {any}                                   |                                      | App\Http\Controllers\ApplicationController                                | web          |

so I guess it would be https://example.org/oauth/authorize, right? But if I open this, I am getting redirected to my app's dashboard.

Apr
12
1 month ago
Activity icon

Replied to One Model, Different Pivot Tables

Hi @bugsysha

Right, the example is a bit too short. The Product does not only contain the ProductAttribute but also a price, a description etc. .

Can you give an example for your suggestion to go with another pivot table?

Activity icon

Replied to One Model, Different Pivot Tables

Correct. To point out the reason:

I would like to define a list of products with attributes. And for usability reasons I would also like to have the possibility of product bundles.

Example:

  • Product is a car with the ProductAttribute color and the pivot-data blue.
  • Product is a steering wheel cover with the ProductAttribute color and the pivot-data brown.

Now a possible bundle should contain both products with different pivot:

Example:

  • Bundle contains two Product
    • Product is a car with the ProductAttribute color and the pivot-data yellow.
    • Product is a steering wheel cover with the ProductAttribute color and the pivot-data black.
Activity icon

Started a new Conversation One Model, Different Pivot Tables

Hello everybody,

I got a problem that is driving me crazy ;-)

Let's start:

The models

I got the following models:

  • Product
  • ProductAttribute
  • Bundle

The relations

Each Product can have many ProductAttribute s with a pivot-value (in this case: value and unit).

This is no problem so far:

/**
 * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
 */
public function productAttributes()
{
    return $this->belongsToMany(ProductAttribute::class)
        ->withPivot('value', 'unit')->withTimestamps();
}

The problem

Now the problem begins:

Each Bundle can also have many Products and each Product belonging to a Bundle should have it's own pivot-table for it's ProductAttribute s.

The dirty solution

So what I am doing at the moment is a pretty ugly solution:

The Product is getting two relations:

/**
 * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
 */
public function productAttributes()
{
    return $this->belongsToMany(ProductAttribute::class)
        ->withPivot('value', 'unit')->withTimestamps();
}

/**
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function productBundleAttributes()
{
    if ($this->pivot) {
        return $this->belongsToMany(ProductAttribute::class,
            'product_bundle_product_attribute')
            ->withPivot('value', 'unit')
            ->wherePivot('product_product_bundle_uuid', '=',
                $this->pivot->uuid)->withTimestamps();
    }
    
    return $this->belongsToMany(ProductAttribute::class,
        'product_bundle_product_attribute')
        ->withPivot('value', 'unit');
}

Now the ProductAttribute model is getting some overwritten getters to decide which pivot to use:

/**
* @param $value
*
* @return bool
*/
public function getUnitAttribute($value)
{
    return $this->isBundle() ? $this->getBundleUnit() : $this->getProductUnit();
}

[..]

public function isBundle()
{
    return ($this->hasPivot()
        && $this->getPivotTable() === 'product_bundle_product_attribute')
         ? true : false;
}

Other approaches

I also thought about creating some kind of "dummy-models" like this:

  • Product -> hasMany ProductAttribute -> withPivot
  • Bundle -> hasMany BundleProduct -> hasMany BundleProductAttribute -> withPivot

But the problem here is that I have to sync BundleProduct and BundleProductAttribute with Product and ProductAttribute.

My question

I am sure there is a better way to solve this. I am happy for all your thoughts on this :-)

Apr
07
1 month ago
Activity icon

Awarded Best Reply on Post Form Input To API Provided By Another Laravel Application.

Actually it's quite easy. It has to be 'form_params' => $payload,

Activity icon

Replied to Post Form Input To API Provided By Another Laravel Application.

Actually it's quite easy. It has to be 'form_params' => $payload,

Activity icon

Started a new Conversation Return Custom Error On Model::create

Hello everybody,

I would like to do the following: A new model should be created by posting the data via API to another server.

The response is a Illuminate\Http\JsonResponse.

This is what I am doing at the moment:

    public static function create($data)
    {
        /*Create a new item using the API*/
        $client = new ApiClient();

        $response = $client->postToApi('/api/item', $data);

        if ($response->getStatusCode() !== 200) {
            return $response;
        }

        return json_decode((string)$response->getBody())->data;
    }

If the submission succeeds, the following response is returned (which is the output of json_decode((string)$response->getBody())->data ):

{#1046
  +"id": 10
  +"country": "de"
  +"homepage": null
  +"status": "active"
}

If the action fails, I am getting this object:

Illuminate\Http\JsonResponse {#1038
  #data: ""{\"message\":\"The given data was invalid.\",\"errors\":{\"data.id\":[\"data.id is not unique.\"]}}""
  #callback: null
  #encodingOptions: 0
  +headers: Symfony\Component\HttpFoundation\ResponseHeaderBag {#1037
    #computedCacheControl: array:2 [
      "no-cache" => true
      "private" => true
    ]
    #cookies: []
    #headerNames: array:3 [
      "cache-control" => "Cache-Control"
      "date" => "Date"
      "content-type" => "Content-Type"
    ]
    #headers: array:3 [
      "cache-control" => array:1 [
        0 => "no-cache, private"
      ]
      "date" => array:1 [
        0 => "Tue, 07 Apr 2020 14:11:01 GMT"
      ]
      "content-type" => array:1 [
        0 => "application/json"
      ]
    ]
    #cacheControl: []
  }
  #content: ""{\"message\":\"The given data was invalid.\",\"errors\":{\"data.id\":[\"data.id is not unique.\"]}}""
  #version: "1.0"
  #statusCode: 422
  #statusText: "Unprocessable Entity"
  #charset: null
  +original: "{"message":"The given data was invalid.","errors":{"data.id":["data.id is not unique."]}}"
  +exception: null
}
=> Illuminate\Http\JsonResponse {#1038
     +headers: Symfony\Component\HttpFoundation\ResponseHeaderBag {#1037},
     +original: "{"message":"The given data was invalid.","errors":{"data.id":["data.id is not unique."]}}",
     +exception: null,
   }

Alright, I can work with this. But: Is there any possibility to make this response more "compatible" to default eloquent models (in success and in failure)?

Apr
06
1 month ago
Activity icon

Started a new Conversation Post Form Input To API Provided By Another Laravel Application.

Hello there :-)

I would like to make a POST-request containing form data to a Laravel-based app, which provides an API.

Actually the remote Laravel-API accepts checks this:

  /*Validate request*/
        $request->validate([
            'data.email'               => 'required|string|email|max:255|unique:users,email',
        ]);

If I now do a POST-request like this:

$payload = [ 'data' => [
                            'email'       => $request->input('data.email'),  
                        ] 
                     ];

$response = $client->request('POST',
                'https://'.config('server').$route, [
                    'headers' => [
                        'Accept'        => 'application/json',
                        'Authorization' => 'Bearer '.$this->getAccessToken(),
                    ],
                    $payload,
                ]);

The validation fails:

"message":"The given data was invalid.","errors":{"email":"The email field is required."

So my question: How can I create a POSTable array that can be validated by the API?

Apr
01
2 months ago
Activity icon

Replied to Invalid Prop: Expected Number With Value NaN

Hi and thanks for your reply. This does not work, too. In this case, the component accepts a string like "1" and the validation of the integer fails (because it's a string ;-) ).

Activity icon

Started a new Conversation Invalid Prop: Expected Number With Value NaN

Hello everybody,

I got a little problem.

I am adding custom form-elements to my SPA. One element is an Integer field:

<template>
    <ValidationProvider :rules="{ required: required, integer: true }" v-bind="$attrs" v-slot="{ errors }">
        <input
            :value="value"
            v-bind="$attrs"
            v-model="innerValue"
        >
    </ValidationProvider>
</template>
<script>
    export default {

        name: 'integer',

        props: {
            name: String,
            value: [Number],
            required: Boolean
        },

        components: {},

        data: () => ({
            innerValue: ''
        }),

        watch: {
            innerValue (newVal) {
                this.$emit ('input', newVal);
            },
            value (newVal) {
                this.innerValue = newVal;
            }
        },

        created () {
            if (this.value) {
                this.innerValue = parseInt(this.value);
            }
        }

    }
</script>

This can be added to a page like this:

<Integer :required="true" v-model="contact.zip"/>

But in this case, I am getting this error:

: Invalid prop: type check failed for prop "value". Expected Number with value NaN, got String with value "1".

So obviously the input made by the user is processed as string.

If I set

        props: {
            name: String,
            value: [Number, String],
            required: Boolean
        },

the error does not appear - but the validation for an integer fails.

How can I deal with that?

Mar
25
2 months ago
Activity icon

Started a new Conversation Broken Markdown-Mail

Hello everybody,

I just mentioned that my Markdown-Mails are broken:

This is a screenshot of the "damaged" mail:

https://imgur.com/nsc5W6a

This is the source of the mail:


--_=_swift_1585122635_6ff9bd6f5f9cd76cd07e623023d0a3bd_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=3D"http://www.=
w3.org/1999/xhtml">
<head>
<meta name=3D"viewport" content=3D"width=3Dd=
evice-width, initial-scale=3D1.0">
<meta http-equiv=3D"Content-Type" cont=
ent=3D"text/html; charset=3DUTF-8">
</head>
<body style=3D"box-sizing: =
border-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Rob=
oto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', '=
Segoe UI Symbol'; position: relative; -webkit-text-size-adjust: none; backg=
round-color: #ffffff; color: #718096; height: 100%; line-height: 1.4; margi=
n: 0; padding: 0; width: 100% !important;">
<style>
@media  only screen=
 and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
=

.footer {
width: 100% !important;
}
}

@media  only screen and=
 (max-width: 500px) {
.button {
width: 100% !important;
}
}
</sty=
le>

<table class=3D"wrapper" width=3D"100%" cellpadding=3D"0" cellspac=
ing=3D"0" role=3D"presentation" style=3D"box-sizing: border-box; font-famil=
y: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,=
 sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; posi=
tion: relative; -premailer-cellpadding: 0; -premailer-cellspacing: 0; -prem=
ailer-width: 100%; background-color: #edf2f7; margin: 0; padding: 0; width:=
 100%;">
<tr>
<td align=3D"center" style=3D"box-sizing: border-box; fon=
t-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,=
 Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol=
'; position: relative;">
<table class=3D"content" width=3D"100%" cellpadd=
ing=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"box-sizing: bord=
er-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,=
 Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Sego=
e UI Symbol'; position: relative; -premailer-cellpadding: 0; -premailer-cel=
lspacing: 0; -premailer-width: 100%; margin: 0; padding: 0; width: 100%;">=

<tr>
<td class=3D"header" style=3D"box-sizing: border-box; font-family=
: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, =
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; posit=
ion: relative; padding: 25px 0; text-align: center;">
<a href=3D"https://=
staging.example.org" style=3D"box-sizing: border-box; font-family: -a=
pple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans=
-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; position:=
 relative; color: #3d4852; font-size: 19px; font-weight: bold; text-decorat=
ion: none; display: inline-block;">
Example.org
</a>
</td>
</=
tr>

<!-- Email Body -->
<tr>
<td class=3D"body" width=3D"100%" cel=
lpadding=3D"0" cellspacing=3D"0" style=3D"box-sizing: border-box; font-fami=
ly: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial=
, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; pos=
ition: relative; -premailer-cellpadding: 0; -premailer-cellspacing: 0; -pre=
mailer-width: 100%; background-color: #edf2f7; border-bottom: 1px solid #ed=
f2f7; border-top: 1px solid #edf2f7; margin: 0; padding: 0; width: 100%;">=

<table class=3D"inner-body" align=3D"center" width=3D"570" cellpadding=
=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"box-sizing: border-=
box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, He=
lvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe U=
I Symbol'; position: relative; -premailer-cellpadding: 0; -premailer-cellsp=
acing: 0; -premailer-width: 570px; background-color: #ffffff; border-color:=
 #e8e5ef; border-radius: 2px; border-width: 1px; box-shadow: 0 2px 0 rgba(0=
, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); margin: 0 auto; padding=
: 0; width: 570px;">
<!-- Body content -->
<tr>
<td class=3D"content-=
cell" style=3D"box-sizing: border-box; font-family: -apple-system, BlinkMac=
SystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color =
Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relative; max-width:=
 100vw; padding: 32px;">
<h1 style=3D"box-sizing: border-box; font-family=
: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, =
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; posit=
ion: relative; color: #3d4852; font-size: 18px; font-weight: bold; margin-t=
op: 0; text-align: left;">Neue Kontaktanfrage</h1>
<pre style=3D"box-sizi=
ng: border-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',=
 Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji=
', 'Segoe UI Symbol'; position: relative;"><code>Es liegt eine neue Kontakt=
anfrage vor

&lt;table class=3D"panel" width=3D"100%" cellpadding=3D"0"=
 cellspacing=3D"0" role=3D"presentation"&gt;
</code></pre>
</td>
</tr=
>
<tr>
<td class=3D"panel-content" style=3D"box-sizing: border-box; fon=
t-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,=
 Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol=
'; position: relative; background-color: #edf2f7; color: #718096; padding: =
16px;">
<table width=3D"100%" cellpadding=3D"0" cellspacing=3D"0" role=3D=
"presentation" style=3D"box-sizing: border-box; font-family: -apple-system,=
 BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'App=
le Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relative;">=

<tr>
<td class=3D"panel-item" style=3D"box-sizing: border-box; font-fa=
mily: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Ari=
al, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; p=
osition: relative; padding: 0;">
<ul style=3D"box-sizing: border-box; fon=
t-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,=
 Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol=
'; position: relative; line-height: 1.4; text-align: left;">
<li style=3D=
"box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, 'S=
egoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe=
 UI Emoji', 'Segoe UI Symbol'; position: relative;">Vorname: Test
- Nachn=
ame: asdasd
- Firma:
- E-Mail: [email protected]
- Nachricht: asdsaasda=
sd</li>
</ul>
</td>
</tr>
</table>
</td>
</tr>
</table>
<pr=
e style=3D"box-sizing: border-box; font-family: -apple-system, BlinkMacSyst=
emFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoj=
i', 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relative;"><code>Danke f=
=C3=BCr eure Aufmerksamkeit,&lt;br&gt;
Example.org
</code></pre>=




</td>
</tr>
</table>
</td>
</tr>

<tr>
<td style=
=3D"box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont,=
 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Se=
goe UI Emoji', 'Segoe UI Symbol'; position: relative;">
<table class=3D"f=
ooter" align=3D"center" width=3D"570" cellpadding=3D"0" cellspacing=3D"0" r=
ole=3D"presentation" style=3D"box-sizing: border-box; font-family: -apple-s=
ystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif=
, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relat=
ive; -premailer-cellpadding: 0; -premailer-cellspacing: 0; -premailer-width=
: 570px; margin: 0 auto; padding: 0; text-align: center; width: 570px;">
=
<tr>
<td class=3D"content-cell" align=3D"center" style=3D"box-sizing: bor=
der-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto=
, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Seg=
oe UI Symbol'; position: relative; max-width: 100vw; padding: 32px;">
<p =
style=3D"box-sizing: border-box; font-family: -apple-system, BlinkMacSystem=
Font, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji'=
, 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relative; line-height: 1.5=
em; margin-top: 0; color: #b0adc5; font-size: 12px; text-align: center;">=
=C2=A9 2020 Example.org. All rights reserved.</p>

</td>
</tr>=

</table>
</td>
</tr>
</table>



</body>
</html>

This is the source code of the used blade-file:

@component('mail::message')
    # Neue Kontaktanfrage

    Es liegt eine neue Kontaktanfrage vor

    @component('mail::panel')
        - Vorname: {{$firstname}}
        - Nachname: {{$lastname}}
        - Firma: {{$company}}
        - E-Mail: {{$email}}
        - Nachricht: {{$message}}
    @endcomponent

    Danke für eure Aufmerksamkeit,<br>
    {{ config('app.name') }}
@endcomponent

This is the mailable:

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Http\Request;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

/**
 * Class ContactSubmitted
 *
 * @package App\Mail
 */
class ContactSubmitted extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * @var mixed
     */
    public $firstname;
    /**
     * @var mixed
     */
    public $lastname;
    /**
     * @var mixed
     */
    public $company;
    /**
     * @var mixed
     */
    public $email;
    /**
     * @var mixed
     */
    public $message;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(Request $request)
    {
      $this->firstname =  $request->input('data.firstname');
      $this->lastname =  $request->input('data.lastname');
      $this->company =  $request->input('data.company');
      $this->email =  $request->input('data.email');
      $this->message =  $request->input('data.message');
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build(Request $request)
    {
        return $this->markdown('emails.contact.submitted');
    }
}

And this is how I send the mail using the controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\ContactSubmitted;

/**
 * Class ContactApiController
 *
 * @package App\Http\Controllers
 */
class ContactApiController extends Controller
{

    /**
     * @param \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\JsonResponse
     */public function send(Request $request)
    {
      /*Validate request*/
       $request->validate([
         'data.firstname'        => 'required|string|max:255',
         'data.lastname'         => 'required|string|max:255',
         'data.company'          => 'nullable|string|max:255',
         'data.email'            => 'required|email|max:255',
         'data.message'          => 'required|string|max:4000',
       ]);

       Mail::to('[email protected]')->send(new ContactSubmitted($request));
       return response()->json(['message' => 'success']);

    }

}

Mar
24
2 months ago
Activity icon

Started a new Conversation Fetch Product In Getter

Hello everybody,

I was using the example provided on https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart. Now this example uses an api which provides some demo data. In my case I am using an actual API like this:

export default {
    getProduct (id) {
        return axios.get (`/api/product/${id}`);
    }
}

Now I am confused: I got a getter like this:

const getters = {
    cartProducts: (state, getters, rootState) => {
        return state.items.map (({id, quantity}) => {
            const product = api.getProduct (id); //this does not work using .then(), too

            return {
                id: product.id,
                title: product.title,
                price: product.price,
                quantity
            }
        })
    },
};

This fails, as axios returns a promise and the getter itself is not supposed to be async.

Now I could imagine to use a action to perform the request. But it seems wrong to dispatch an action in the getter? I am very thankful for your hints :-)

Mar
17
2 months ago
Activity icon

Replied to [vuex] Module Namespace Not Found In MapActions()

Actually I can not reach the vuex docs at the moment (tbh I think there are connection issues because that many ppl are on HomeOffice at the moment)... :-(

I am very thankful for your help.

I changed it to

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations,
}

Now I am getting this error:

[vuex] unknown local action type: addProductToCart, global type: cart/addProductToCart

Additionally my old getters do no longer work:

computed: {
            ...mapState({
                checkoutStatus: state => state.cart.checkoutStatus
            }),
            ...mapGetters('cart', {
                products: 'cartProducts',
                total: 'cartTotalPrice'
            })
        },

This throws

[vuex] unknown getter: cart/cartProducts

Activity icon

Started a new Conversation [vuex] Module Namespace Not Found In MapActions()

Hello,

I would like to add this method to a component:

        methods: mapActions ('cart', [
            'addProductToCart'
        ]),

If I do so, I am getting this error:

[vuex] module namespace not found in mapActions()

This is my store.js:

import Vue from 'vue';
import Vuex from 'vuex';

import promotion from './modules/products'
import cart from './modules/cart'

Vue.use (Vuex);

export const store = new Vuex.Store ({
    modules: {
        products, cart
    }
});

And this is the cart.js:

const state = {
    status: '',
    items: [],
};

const getters = {
    cartProducts: (state, getters, rootState) => {
        return state.items.map (({id, quantity}) => {
            const product = rootState.products.all.find (product => product.id === id);
            return {
                title: product.title,
                price: product.price,
                quantity
            }
        })
    },
    cartTotalPrice: (state, getters) => {
        return getters.cartProducts.reduce ((total, product) => {
            return total + product.price * product.quantity
        }, 0)
    }
};

const actions = {
    checkout ({commit, state}, products) {
        const savedCartItems = [...state.items];
        commit ('setCheckoutStatus', null);
        // empty cart
        commit ('setCartItems', {items: []});
        shop.buyProducts (
            products,
            () => commit ('setCheckoutStatus', 'successful'),
            () => {
                commit ('setCheckoutStatus', 'failed');
                // rollback to the cart saved before sending the request
                commit ('setCartItems', {items: savedCartItems})
            }
        )
    },
    addProductToCart ({state, commit}, product) {
        commit ('setCheckoutStatus', null);
        if (product.inventory > 0) {
            const cartItem = state.items.find (item => item.id === product.id);
            if (!cartItem) {
                commit ('pushProductToCart', {id: product.id})
            } else {
                commit ('incrementItemQuantity', cartItem)
            }

            // remove 1 item from stock
            commit ('products/decrementProductInventory', {id: product.id}, {root: true})
        }
    }
};

const mutations = {
    pushProductToCart (state, {id}) {
        state.items.push ({
            id,
            quantity: 1
        })
    },

    incrementItemQuantity (state, {id}) {
        const cartItem = state.items.find (item => item.id === id);
        cartItem.quantity++
    },

    setCartItems (state, {items}) {
        state.items = items
    },

    setCheckoutStatus (state, status) {
        state.status = status
    }
};

export default {
    state,
    getters,
    actions,
    mutations,
}

What did I miss? Thanks for your help :-)