willvincent

willvincent

Member Since 4 Years Ago

Central Minnesota

Experience Points
334,805
Total
Experience

0 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
1579
Lessons
Completed
Best Reply Awards
330
Best Reply
Awards
  • start-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-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-token Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer-token Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • lara-evanghelist 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 50
334,805 XP
Nov
12
18 hours ago
Activity icon

Replied to Postgres, Eloquent And Swedish Characters

I's start with this, it's almost certainly relevant: https://tech.willandskill.se/create-a-database-with-different-encoding/

Are you on a mac? Apparently the Swedish locale is just a symlink to US-ASCII on mac, or was on some versions of mac os.. so might be your issue.

Seems relevant: https://dba.stackexchange.com/a/46409

Activity icon

Replied to Somethings Wrong With A Fresh Laravel 6 Terminal Outputs

Not .env.. the env command, in bash.. unless you're using windows' cmd instead which is probably the entire issue in and of itself. but bash, even git bash, ought to properly support ansi escape codes.

?[33mOptions:?[39m

The ?[ represents the start of an escape string, 33m sets the foreground color to yellow, 39m sets it to the default/white.

This is why explicitly setting the --no-ansi flag prevents the output, but it oughtn't happen if your terminal is set to the appropriate terminal emulation mode, allowing ansi escape codes to work properly. Suspect given this is associated with a specific version of symfony console that version introduced a bug preventing proper detection of terminal ansi support or something that was otherwise previously disabling the ansi sequences automatically.

Was a windows user for years, but between the past several years working on a mac, and many years working with various linux flavors... I could never go back to using windows for dev. ... and I tried just within the past month. I just can't get a comfortable environment in windows the way I can with either of the other two. Dunno how people do it...

Nov
11
1 day ago
Activity icon

Replied to Somethings Wrong With A Fresh Laravel 6 Terminal Outputs

Those are escape codes, looks to me like the environment isn't configured to display colors/etc, but it's still outputing the ansi escape codes to set output colors.

Curious. @lemmon what do you have for TERM in the output of env?

Activity icon

Replied to How To Unistall Package Which I've Already Installed ?

I do not like put efforts

Noted, now we know to not bother putting effort into answering your questions in the future. :)

Nov
05
1 week ago
Activity icon

Awarded Best Reply on HTML To Image

Can be done client side with javascript, just have to render html in a canvas element, then do a canvas .toDataURL() call to generate png, jpg, etc.

Nov
04
1 week ago
Activity icon

Replied to HTML To Image

Can be done client side with javascript, just have to render html in a canvas element, then do a canvas .toDataURL() call to generate png, jpg, etc.

Activity icon

Replied to Update Multiple Records At A Time Using Checkbox

So what is your question?

What have you tried?

Activity icon

Replied to How To Get A Specific The Meta Tag Content If It Exists?

This is a pretty poorly written question, but I assume what you're wanting to do is along the lines of what facebook and twitter do when you post a link -- reach out to that link and grab relevant metadata from the markup served when hitting the link.

PHP's get_meta_tags() function ought to be able to pull that for you without issue...

For example using the Guzzle client to fetch the url:

// Fetch the url with guzzle
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'http://example.com');

// Extract the body from the response data
$content = $response->body();

// Extract meta tags from the body
$tags = get_meta_tags($content);

// if the 'itemprop' meta tag exists...
if ($tags['itemprop']) {
  // set result to its value...
  $result = $tags['itemprop'];
}

Goute would work too as @nakov mentioned, but is probably major overkill given the desired target is specifically meta tags

Activity icon

Replied to Laracasts Improvements Discussion

Timezones

I also regularly see wildly inaccurate date information and I'm in US Central time.. so I'm not convinced the issue(s) is/are tied to timezones.

Leaderboards

I don't know who specifically you're referring to but I certainly don't believe "half of the users" just have all the lessons marked complete. There are definitely some that aren't as active in the forums as they once were (myself included to a certain degree), but I don't believe it's nearly as skewed as you believe it is.

Locking of old threads

Not sure if outright locking and prevention of further discussion is quite the right approach, but maybe some kind of "Are you sure you want to revive this thread" kind of message reminding the user that it's rather old and likely they're better off starting a new thread instead would be beneficial. It is certainly annoying to get a notification of activity on a thread you participated in years ago, often just because someone decides to spam their new package to a bunch of barely relevant old threads and so forth.

Prefixing thread titles

Some manner of indicating the relevant laravel version would be useful, but I don't think the assumption can be made that it would be whatever was the current version at the time of posting, and my experience has shown that users generally can't be fully relied upon to provide all the relevant information when seeking assistance. Maybe the inclusion of a "Select the version of laravel" widget on the post creation form would help, but it may likely just get ignored, or wrongly assigned too.. no real silver bullets here.

Thread suggestions upon thread creation

This would be an interesting feature.. Would require some sort of recommendation engine, based on.. what? The entered post title? That leads to another major issue with user-generated content, fairly worthless titles :) But interesting feature, if that nut can be cracked in an intelligent manner.

Forum Titles

Don't the existing badges already more or less accomplish this?

Caching issues

Not sure I've experienced the behavior you mention here, but even if I had I'm not overly bothered by a bit of delay due to caching, the site feels speedy, and it's not ever been advertised as real-time communication, so if caching keeps the site speedy, and operating costs down for Jeffrey, I'm not personally bothered by any caching delays.. but that's just one mans' opinion :)

Oct
28
2 weeks ago
Activity icon

Replied to Many-to-many From The Same Table

If you want to associate other data with your pivot, you'll probably want to extend the base pivot class with a custom Pivot Model...

Also, by the way, it's usually better to ask a new question than to revive a many-years-old thread ;)

Oct
25
2 weeks ago
Activity icon

Awarded Best Reply on Own Rating In List Of Entries

Don't pre-emptively optimize. You're overthinking this.

Oct
23
2 weeks ago
Activity icon

Replied to Own Rating In List Of Entries

Don't pre-emptively optimize. You're overthinking this.

Activity icon

Replied to Can I Completely Separate Vue.js Front-end To Laravel Back-end

Just off the top of my head... some pros and cons.

PRO:

  • Fully decoupled apps are more easily scalable
  • Standalone vue app can easily utilize a CDN
  • Additional backend services can be added via other backends, instead of continuously growing your laravel backend code

CON:

  • Generally the application will be 'stateless' and authentication done via JWT token, which can be a little more challenging
  • Permissions/routing/etc is sort of double effort since the laravel side would most likely end up just being an api, and vue would need to also enforce who has access to what frontend routes/etc. This can cause some level of duplicate effort

It's probably not necessary for most people/most applications to fully decouple, and it can be more work at least initially.

There are benefits to both methods, ultimately it really comes down to what you need/want to do. Can always change things later too.

If you do go the route of a completely separate vue application for your frontend, I really cannot recommend netlify highly enough for hosting of the vue portion.. it is the best for that sort of thing.

Activity icon

Replied to CRUD

@palypster Please don't spam your link all over tons of old posts

Activity icon

Replied to Make V-for Print Only One Object

@ftiersch has the right solution.

Use a computed property to create a filtered list to iterate over with a v-for, and if that list is empty, display your empty message, else the v-for.

ie:

<div>
  <p v-if="todaySantas.length < 1">No Santas today. :(</p>
  <div v-else>
    <ul>
      <li v-for="santa in todaySantas" :key="santa.id">{{ santa.name }}</li>
    </ul>
  </div>
</div>

and so forth.

Activity icon

Replied to How Do I Keep Getting Hacked On PHPUnit?

If PHPUnit is the vulnerability, easy fix -- remove phpunit.

PHPUnit is a dev package requirement, and should be installed as such. No dev packages need nor should be installed in production.

Activity icon

Replied to Own Rating In List Of Entries

Seems like you ought to be able to pass in ids from the cached list as a whereIn() clause on a query against the authenticated user's ratings. Then you could merge the results of that query with the cached list and send a modified version of the cached data in your response.

I can't imagine it would need to be any more complicated than that

Oct
18
3 weeks ago
Activity icon

Replied to Vue Component In A Form

Since vue is adding items to the dom, so long as you're adding fields with names, etc.. inputs/checkboxes/radios/etc.. whatever you set for the name on those fields should be accessible within your controller in the request object.

Would probably be more helpful to see what you're doing, what you've tried that isn't working, etc.

Activity icon

Replied to Numbering Other Than Increment

In the migration set it to $table->bigInteger('id')->unsigned()->index();

In the model set: public $incrementing = false;

Then setup an event listener that listens for the eloquent "creating" event on that model, and use your own custom logic to determine the next id and set it.

Frankly though, I think you'd be much better off defining a type and using a where clause, rather than trying to determine who is what sort of user based on whether their ID is even or odd, that's really rather strange.

Oct
16
3 weeks ago
Activity icon

Replied to Laracasts - Thoughts? Opinions? Roadmap To Proper Use?

If you're new to laravel, start with the newest laravel from scratch series, then work through others as you want/need to cover other topics.

The vue series' are really good.

Worth it? Yes, definitely. Jeffrey is one of the best teachers around, hands down. I kind of wish I'd have just gone for the lifetime membership originally, though at that time I think it required manually reaching out to Jeffrey unlike today where it's just an option on the signup page lol. Maybe I'll switch one of these days, now that I've spent more on annual subscriptions than the cost of lifetime membership..

Activity icon

Replied to Any Difference Between Defining Relationships With 'App\Model' And Model::class

I think the only situation in which it would actually matter would be if the models weren't all in the same namespace, in which case you'd (I think) need to explicitly use the models being referenced using the Model::class format.

Personal preference I guess. Though 'App\Profile' is one fewer character. :D

Oct
01
1 month ago
Activity icon

Replied to How To Collect Related Data Through Pivot Table?

Given that workers belong to one branch, and users belong to many branches, something like this should be sufficient:

$user = User::findOrFail($userId)

$workers = Worker::whereIn('branch', $user->branches()->pluck('id')->get())->all()

I'm a little rusty so please ignore any eloquent syntax issue there.

Activity icon

Replied to Laravel | Copy To Clipboard Button

<div class="row">
    @foreach($photos as $photo)
         <div class="col-md-3" style="padding:5px;">
              <div class="card">
                   <img src="/project/storage/app/{{ $photo->filename }}" class="card-img-top">
                   <div class="card-body">
                         <input type="text" id="copy_{{ $photo->id }}" value="{{ $photo->filename }}">
                         <button value="copy" onclick="copyToClipboard('copy_{{ $photo->id }}')">Copy!</button>
                  </div>
             </div>
      </div>
  @endforeach
</div>
<script>
    function copyToClipboard(id) {
        document.getElementById(id).select();
        document.execCommand('copy');
    }
</script>

That oughtta do it

Activity icon

Replied to Overriding Relationship Behavior

It sounds like you're referencing this wrongly in your templates if you're getting the relation rather than it's value (or null)...

Are you eager loading? If not, you almost certainly ought to be.

Activity icon

Replied to Cron In Vapor

Normal cloudwatch event limits would apply... probably the most relevant one would be the limit of 100 rules per region per account. That's a soft limit though, you can request an increase if necessary.

That's still probably way more scheduled events than most people will have

See: Cloudwatch Event Limits

Activity icon

Replied to Overriding Relationship Behavior

Curious why you wouldn't instead just make that foreign key field nullable, and use null instead of a 0 id.. ?

Sep
30
1 month ago
Activity icon

Replied to Issue Template

The issue template will prepopulate the textarea when opening a new issue. It ensures you get comprehensive, relevant data to debug and fix errors people report.

Sep
26
1 month ago
Activity icon

Replied to Must Have Plugins

@albertski You certainly don't need other packages like you do for Drupal, but I'd suggest as you go about building out features to look around for existing solutions, that have lots of downloads and recent commits/etc to see if there is potentially something that'll save time.

I'd guess the vast majority of your effort though will be on actually writing code, not cobbling together different pieces like you did with Drupal.

I much prefer working with Laravel than Drupal -- for a bit of context around that statement, I was one of the first 25 people to earn the Drupal "Grand Master" certification ;)

EDIT: I never looked at the registry in full before, sorting by date awarded, I'm actually #5 :D lol

Activity icon

Replied to Need To Order Posts Based On A Composite Score

Another reason (besides ease of use) the cron option is good to keep in mind, as your dataset grows, the various calculations involved in your sort will occupy more time, so hiding that calculation latency from users, by introducing some overall 'freshness' latency could be a very necessary speed enhancement too...

Activity icon

Replied to Need To Order Posts Based On A Composite Score

@whoisthisstud It's generally best to do sorting within the DB whenever possible, especially if you want to paginate (which you almost always should want to do)...

The other option not yet discussed that would be reasonable would be to precalculate the sorting score via a cron job, so every 5/10/15/30 minutes, or whatever, re-run a job that calculates the score for all the records, and stores that value so it can be easily incorporated into a query.

There would be some lag time between sort ordering getting updated, but the drastic ease of querying may be a worthwhile tradeoff to some of the realtimeyness

Activity icon

Replied to Need To Order Posts Based On A Composite Score

Given the complexity of the sql, probably easier (and arguably better) to just leave it as sql than trying to force it to conform to eloquent query builder syntax.

Sep
25
1 month ago
Activity icon

Replied to Different Types Of Tags/categories For A Model

You could override the default syncTagsWithType method and make it do a findOrFail instead.

It lives in the HasTags trait, I'd suggest creating your own HasTags trait that extends the package's trait, and override just that one method, or others if necessary.

Something like this ought to do the job..

use Spatie\Tags\HasTags as BaseHasTags;

trait HasTags
{
    use BaseHasTags;

    /**
     * @param array|\ArrayAccess $tags
     * @param string|null $type
     *
     * @return $this
     */
    public function syncTagsWithType($tags, string $type = null)
    {
        $className = static::getTagClassName();
        $tags = collect($className::findOrFail($tags, $type));
        $this->syncTagIds($tags->pluck('id')->toArray(), $type);
        return $this;
    }
}

There are several other methods in there that use a findOrCreate, you may or may not need/want to override those too.

Sep
23
1 month ago
Activity icon

Replied to Trait Method Conflict

Yeah I kind of assumed so, but saw sample code both ways :) and am not currently in a place to putz around with actually writing/testing php code.

Activity icon

Replied to Different Types Of Tags/categories For A Model

Good choice, didn't realize there was a spatie package, that's definitely the way to go if you're using a package, and they have one available.

Activity icon

Replied to Trait Method Conflict

@zefex Actually you'll probably also have to replicate the conditionals from both traits too:

    public function getAttributeValue($value)
    {
        if ($this->isTranslatableAttribute($value)) {
            return translationGetAttributeValue($value);
        }
        if ($this->hasEnumCast($value)) {
            return enumGetAttributeValue($value);
        }
        return parent::getAttributeValue($value);
    }
    public function setAttribute($key, $value)
    {
        if ($this->isTranslatableAttribute($key)) {
            return translationSetAttribute($key, $value);
        }
        if ($value !== null && $this->hasEnumCast($key)) {
            return enumSetAttribute($key, $value);
        }
        return parent::setAttribute($key, $value);
    }

Not sure off hand if $this-> is necessary before calling those aliased trait methods.

Activity icon

Replied to Different Types Of Tags/categories For A Model

Dunno that it's really necessary, but yeah there is laravel-categories .. and a few others, but this appears to be the most popular, and was last updated today.

I think you could probably get by with your tags/categories having a 'type' flag, assuming you'll have a (even if large) limited number of types, in your example you have services and payment types, so lets go with that...

Your tags could either belongTo a parent Type as @martinbean is suggesting, or you could just as easily use an enum or a string value, especially if the number of types will be limited.

Then for ease you'd probably want your ads to have multiple relationships for the various types, ie:

class Ad extends Model {
  public function services() {
    return $this->belongsToMany(Tag::class)
                ->where('type', 'service')
  }

  public function paymentMethods() {
    return $this->belongsToMany(Tag::class)
                ->where('type', 'payment_method')
  }
}

Or, it may make sense to just go ahead and keep payment_methods and services totally separated as their own models. That's probably the route I'd go just based on the limited details you've provided as it's likely that payment_methods will have other stuff associated with them, and likewise, perhaps, will services. Those strike me more as different types of entity than simple tags.

Activity icon

Replied to In Vue How To Get The Child Data Into Parent Component

@mariohbrino is right, use the .sync modifier

The only extra clarification I'd add is do not directly modify your props.

Here's an example from some code I have in production:

In the parent component:

<tag-picker :tags.sync="tags" />

In the child component (TagPicker):

export default {
  props: {
    tags: { type: Array, default: () => [] },
  },

  data() {
    return {
      internalTags: [], // used internally, as the v-model
    };
  },

  // This watcher fires immediately, so we don't need a created() hook
  // Every time the tags prop is updated, the internalTags get synced
  // so state is properly maintained
  watch: {
    tags: {
      handler(newValue) {
        this.internalTags = [...newValue];
      },
      immediate: true,
    },
  },

  methods: {
    async addTag(tag) {
      // some stuff is done here to validate/ensure proper format/etc.. then:
      this.$emit('update:tags', this.internalTags);
    },
};

I cut out all the other stuff going on in this component just to focus on the part that's relevant to this discussion.

Since I passed in the tags prop with the .sync modifier, vue is expecting an update:tags event to be emit with a new value for that prop, when that occurs, the value is updated within the parent, which is then naturally passed back into the child component, at which point the watcher sees it and re-syncs the internal data value the child component is able to manipulate.

addTag() in this case is triggered when a user has added a new tag to an input field and hit return to 'submit' it.

Activity icon

Replied to Help Me About Eager Loading.

You might need to watch it again... he specifically covers this problem at the 22min mark: https://www.youtube.com/watch?v=IBUXXErAtuk#t=22m00s

"Create a dynamic relationship" etc.

You'll need the addSubSelect macro he covers earlier in the video to make it work...

use Illuminate\Database\Eloquent\Builder;

Builder::macro('addSubSelect', function ($column, $query) {
    if (is_null($this->getQuery()->columns)) {
        $this->select($this->getQuery()->from.'.*');
    }

    return $this->selectSub($query->limit(1)->getQuery(), $column);
});
Activity icon

Replied to Trait Method Conflict

I suspect what you'd ultimately want to do here is rename the trait's methods, then implement your own getAttributeValue() and setAttribute() methods and have your implementation call the renamed trait implementations..

Something like this:

class Foo extends Model {
  use CastsEnums , HasTranslations {
    CastsEnums::getAttributeValue as enumGetAttributeValue;
    CastsEnums::setAttribute as enumSetAttribute;
    HasTranslations::getAttributeValue as translationGetAttributeValue;
    HasTranslations::setAttribute as translationSetAttribute;
  }

  public function getAttributeValue($value) {
    $this->enumGetAttributeValue($value);
    $this->translationGetAttributeValue($value);
  }

  public function setAttribute($key, $value) {
    $this->enumSetAttribute($key, $value);
    $this->translationSetAttribute($key, $value);
  }
}

Not tested, but seems like it ought to be in the ballpark

Activity icon

Replied to MYSQL And MariaDB

As far as I'm aware, Maria's json column type is compatible with mysql's json column type, and has been since mariadb 10.2.something

"better" is subjective.. at the end of the day the only real notable difference between Mysql and maria is maria is fully open source, community-driven, whereas mysql is owned and managed by oracle.

Activity icon

Replied to Help Me About Eager Loading.

Would highly recommend watching Jonathan Reinink's Laracon presentation, he specifically covers this topic in great detail.. beyond just "hey, eager load your stuff" :)

https://www.youtube.com/watch?v=IBUXXErAtuk

Watch the whole thing as it's all really good... but the specifically relevant portion begins around 20:45

Activity icon

Replied to Print Ticket After Adding A Data

Looks like that is (or at least at some point was ) possible with Chrome in kiosk mode with kiosk-printing enabled.

See: https://www.pos-tpv.com/en/smartblog/7_enable-kiosk-silent-printing-for-google-chrom.html

Sep
22
1 month ago
Activity icon

Replied to Print Ticket After Adding A Data

I don't know if there are/would be any real specifics to printing with a thermal printer vs a regular print method, but assuming you can do it like you would any directly-attached printer, I would redirect to a show page, as @cronix suggested, that has an appropriate print stylesheet, and then maybe trigger window.print() in a javascript on load.

Activity icon

Replied to Print Invoice Plugin In Laravel

You really didn't need to start another thread for this... when you already have the same discussion happening here: https://laracasts.com/discuss/channels/laravel/print-ticket-after-adding-a-data

Activity icon

Replied to Convert This SQL To Laravel

Any particular reason you don't want to use the raw sql?

It's project specific of course, but I generally recommend not using commands that are specific to only certain sql server engines.. neither of your examples work in mysql, for example, nor would they in sqlite, etc.

Sep
17
1 month ago
Activity icon

Replied to Vuex , How Make A Betther Code

I haven't used it, but this looks like it may do everything you need: https://github.com/JiriChara/vuex-crud

Activity icon

Replied to How To Customize Working Hours For A Project In Laravel

The easiest option, probably, would be to have a single table with columns for opening/closing times for each of the seven days of the week.

Something like this:

        Schema::create('working_hours', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->time('sunday_open');
            $table->time('sunday_close');
            $table->time('monday_open');
            $table->time('monday_close');
            $table->time('tuesday_open');
            $table->time('tuesday_close');
            $table->time('wednesday_open');
            $table->time('wednesday_close');
            $table->time('thursday_open');
            $table->time('thursday_close');
            $table->time('friday_open');
            $table->time('friday_close');
            $table->time('saturday_open');
            $table->time('saturday_close');
            $table->timestamps();
        });

For your 08:00 - 16:00, just add those as you would expect.. for the other one create two entries, 0:800 - 12:00 and another row with the afternoon times 14:00 - 18:00

That's probably the easiest way to model that data

EDIT: Those of course should all be nullable, :)

Activity icon

Replied to What Is The Best Way To Manage An Unregistered Visitor Rating System? Token? IP??

There's not really any perfect solution to this because whatever method you choose will depend on an identifier being unique to an unauthenticated user and remaining so. But you can't necessarily depend on a user's IP address remaining the same, nor that a single IP address is one user or the same user(s) each time it's observed. For example; all users in a large office building may very likely have a single shared public IP address, or mobile phone users IP addresses will routinely get assigned to others on the network, etc.

And as for cookies, nothing is preventing a user from deleting their cookies at any point, or that the user is even allowing cookies.

However -- I would suggest using cookies to prevent duplicate ratings from one user, and just accept that there may be some margin of error if a user clears their cookies/etc. That's more likely to be unique to at least a single device than would an IP address..

Interestingly this was a challenge I was tasked with as a junior developer many years ago, that was a bit more involved than just a rating, instead it was a poll system, so a little more involved but basically just additional choices rather than just a +/- :)