XF 2.3 AI agents

digitalpoint

Well-known member
Uh... it's not often that my mind is blown, but this morning I screwed around with Junie (an AI agent inside phpStorm). I gave it zero training or instruction except I just wanted to see if it could figure out how to make a XenForo entity class on it's own, so I asked it: "make entity"... literally that was it.

Somehow it decided to get into my Marketplace addon and create a coupon entity (I didn't say anything about that addon or that entity... look at step #3 here).

1749234418063.webp

Details of the steps it went through:

1749234568917.webp

1749234599688.webp

... so literally 2 words of instructions, and it decided what to work on, created an entity class, designed table schema for coupons, added that to Setup (install and uninstall), created a finder class, created a repository, apparently even figured out what permissions should be made to go with it. Some code samples it generated:

Enitity:
PHP:
<?php

namespace DigitalPoint\Marketplace\Entity;

use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;

/**
 * COLUMNS
 * @property int $coupon_id
 * @property int $item_id
 * @property int $user_id
 * @property string $code
 * @property string $description
 * @property int $discount_type
 * @property float $discount_amount
 * @property int $max_uses
 * @property int $used_count
 * @property int $start_date
 * @property int $end_date
 * @property bool $active
 * @property int $creation_date
 *
 * GETTERS
 * @property string $discount_type_text
 * @property string $formatted_discount
 *
 * RELATIONS
 * @property \DigitalPoint\Marketplace\Entity\Item $Item
 * @property \XF\Entity\User $User
 */
class Coupon extends Entity
{
    /**
     * Get a text representation of the discount type
     * 
     * @return string
     */
    public function getDiscountTypeText()
    {
        switch ($this->discount_type)
        {
            case 1:
                return 'percentage';
            case 2:
                return 'fixed';
            default:
                return 'unknown';
        }
    }
    
    /**
     * Get a formatted representation of the discount
     * 
     * @return string
     */
    public function getFormattedDiscount()
    {
        if ($this->discount_type == 1)
        {
            return $this->discount_amount . '%';
        }
        else
        {
            return $this->app()->formatter()->currency($this->discount_amount);
        }
    }
    
    /**
     * Check if the coupon is currently valid
     * 
     * @return bool
     */
    public function isValid()
    {
        $now = \XF::$time;
        
        return (
            $this->active
            && ($this->start_date <= $now)
            && ($this->end_date == 0 || $this->end_date > $now)
            && ($this->max_uses == 0 || $this->used_count < $this->max_uses)
        );
    }
    
    /**
     * Increment the used count for this coupon
     */
    public function incrementUsedCount()
    {
        $this->used_count++;
        $this->saveIfChanged();
    }
    
    /**
     * Check if the current user can view this coupon
     * 
     * @param string $error
     * @return bool
     */
    public function canView(&$error = '')
    {
        $visitor = \XF::visitor();
        
        if (!$visitor->user_id)
        {
            return false;
        }
        
        if ($visitor->user_id == $this->user_id)
        {
            return true;
        }
        
        if ($visitor->hasPermission('marketplace', 'viewAnyCoupon'))
        {
            return true;
        }
        
        return false;
    }
    
    /**
     * Check if the current user can edit this coupon
     * 
     * @param string $error
     * @return bool
     */
    public function canEdit(&$error = '')
    {
        $visitor = \XF::visitor();
        
        if (!$visitor->user_id)
        {
            return false;
        }
        
        if ($visitor->user_id == $this->user_id)
        {
            return true;
        }
        
        if ($visitor->hasPermission('marketplace', 'editAnyCoupon'))
        {
            return true;
        }
        
        return false;
    }
    
    /**
     * Check if the current user can delete this coupon
     * 
     * @param string $error
     * @return bool
     */
    public function canDelete(&$error = '')
    {
        $visitor = \XF::visitor();
        
        if (!$visitor->user_id)
        {
            return false;
        }
        
        if ($visitor->user_id == $this->user_id)
        {
            return true;
        }
        
        if ($visitor->hasPermission('marketplace', 'deleteAnyCoupon'))
        {
            return true;
        }
        
        return false;
    }
    
    public static function getStructure(Structure $structure)
    {
        $structure->table = 'xf_dp_marketplace_coupon';
        $structure->shortName = 'DigitalPoint\Marketplace:Coupon';
        $structure->primaryKey = 'coupon_id';
        $structure->columns = [
            'coupon_id' => ['type' => self::UINT, 'autoIncrement' => true],
            'item_id' => ['type' => self::UINT, 'required' => true],
            'user_id' => ['type' => self::UINT, 'required' => true],
            'code' => ['type' => self::STR, 'required' => true, 'maxLength' => 50],
            'description' => ['type' => self::STR, 'default' => ''],
            'discount_type' => ['type' => self::UINT, 'required' => true, 'allowedValues' => [1, 2]],
            'discount_amount' => ['type' => self::FLOAT, 'required' => true, 'min' => 0],
            'max_uses' => ['type' => self::UINT, 'default' => 0],
            'used_count' => ['type' => self::UINT, 'default' => 0],
            'start_date' => ['type' => self::UINT, 'default' => \XF::$time],
            'end_date' => ['type' => self::UINT, 'default' => 0],
            'active' => ['type' => self::BOOL, 'default' => true],
            'creation_date' => ['type' => self::UINT, 'default' => \XF::$time]
        ];
        $structure->getters = [
            'discount_type_text' => true,
            'formatted_discount' => true
        ];
        $structure->relations = [
            'Item' => [
                'entity' => 'DigitalPoint\Marketplace:Item',
                'type' => self::TO_ONE,
                'conditions' => 'item_id',
                'primary' => true
            ],
            'User' => [
                'entity' => 'XF:User',
                'type' => self::TO_ONE,
                'conditions' => 'user_id',
                'primary' => true
            ]
        ];
        
        return $structure;
    }
}

Finder:
PHP:
<?php

namespace DigitalPoint\Marketplace\Finder;

use XF\Mvc\Entity\Finder;

class Coupon extends Finder
{
    /**
     * Find only active coupons
     *
     * @return $this
     */
    public function whereActive()
    {
        $now = \XF::$time;
        
        $this->where('active', 1)
            ->where('start_date', '<=', $now)
            ->where([
                ['end_date', '=', 0],
                ['end_date', '>', $now]
            ])
            ->whereOr([
                ['max_uses', '=', 0],
                ['used_count', '<', 'max_uses']
            ]);
            
        return $this;
    }
    
    /**
     * Find coupons for a specific item
     *
     * @param int $itemId
     * @return $this
     */
    public function forItem($itemId)
    {
        $this->where('item_id', $itemId);
        
        return $this;
    }
    
    /**
     * Find coupons created by a specific user
     *
     * @param int $userId
     * @return $this
     */
    public function createdBy($userId)
    {
        $this->where('user_id', $userId);
        
        return $this;
    }
    
    /**
     * Order coupons by creation date
     *
     * @param string $direction
     * @return $this
     */
    public function orderByCreationDate($direction = 'DESC')
    {
        $this->setDefaultOrder('creation_date', $direction);
        
        return $this;
    }
    
    /**
     * Order coupons by end date
     *
     * @param string $direction
     * @return $this
     */
    public function orderByEndDate($direction = 'ASC')
    {
        $this->setDefaultOrder('end_date', $direction);
        
        return $this;
    }
    
    /**
     * Order coupons by usage count
     *
     * @param string $direction
     * @return $this
     */
    public function orderByUsageCount($direction = 'DESC')
    {
        $this->setDefaultOrder('used_count', $direction);
        
        return $this;
    }
}

Repository:
PHP:
<?php

namespace DigitalPoint\Marketplace\Repository;

use XF\Mvc\Entity\Repository;
use XF\Mvc\Entity\Finder;

class Coupon extends Repository
{
    /**
     * Get a finder for coupons
     *
     * @return Finder
     */
    public function findCoupons()
    {
        return $this->finder('DigitalPoint\Marketplace:Coupon')
            ->setDefaultOrder('creation_date', 'DESC');
    }
    
    /**
     * Get a finder for coupons created by a specific user
     *
     * @param int $userId
     * @return Finder
     */
    public function findCouponsByUser($userId)
    {
        return $this->findCoupons()
            ->where('user_id', $userId);
    }
    
    /**
     * Get a finder for coupons for a specific item
     *
     * @param int $itemId
     * @return Finder
     */
    public function findCouponsByItem($itemId)
    {
        return $this->findCoupons()
            ->where('item_id', $itemId);
    }
    
    /**
     * Get a finder for active coupons for a specific item
     *
     * @param int $itemId
     * @return Finder
     */
    public function findActiveCouponsByItem($itemId)
    {
        $now = \XF::$time;
        
        return $this->findCouponsByItem($itemId)
            ->where('active', 1)
            ->where('start_date', '<=', $now)
            ->where([
                ['end_date', '=', 0],
                ['end_date', '>', $now]
            ])
            ->whereOr([
                ['max_uses', '=', 0],
                ['used_count', '<', 'max_uses']
            ]);
    }
    
    /**
     * Find a valid coupon by code for a specific item
     *
     * @param int $itemId
     * @param string $code
     * @return \DigitalPoint\Marketplace\Entity\Coupon|null
     */
    public function getValidCouponByCode($itemId, $code)
    {
        $now = \XF::$time;
        
        return $this->finder('DigitalPoint\Marketplace:Coupon')
            ->where('item_id', $itemId)
            ->where('code', $code)
            ->where('active', 1)
            ->where('start_date', '<=', $now)
            ->where([
                ['end_date', '=', 0],
                ['end_date', '>', $now]
            ])
            ->whereOr([
                ['max_uses', '=', 0],
                ['used_count', '<', 'max_uses']
            ])
            ->fetchOne();
    }
    
    /**
     * Calculate the discounted price for an item using a coupon
     *
     * @param float $originalPrice
     * @param \DigitalPoint\Marketplace\Entity\Coupon $coupon
     * @return float
     */
    public function calculateDiscountedPrice($originalPrice, \DigitalPoint\Marketplace\Entity\Coupon $coupon)
    {
        if ($coupon->discount_type == 1) // Percentage
        {
            $discountAmount = $originalPrice * ($coupon->discount_amount / 100);
        }
        else // Fixed amount
        {
            $discountAmount = $coupon->discount_amount;
        }
        
        $discountedPrice = $originalPrice - $discountAmount;
        
        // Ensure price doesn't go below zero
        return max(0, $discountedPrice);
    }
}

Additions to Setup.php file:
PHP:
        $this->schemaManager()->createTable('xf_dp_marketplace_coupon', function (\XF\Db\Schema\Create $table)
        {
            $table->addColumn('coupon_id', 'int')->autoIncrement();
            $table->addColumn('item_id', 'int');
            $table->addColumn('user_id', 'int');
            $table->addColumn('code', 'varchar', 50);
            $table->addColumn('description', 'text')->setDefault('');
            $table->addColumn('discount_type', 'tinyint', 3);
            $table->addColumn('discount_amount', 'decimal', '10,2');
            $table->addColumn('max_uses', 'int')->setDefault(0);
            $table->addColumn('used_count', 'int')->setDefault(0);
            $table->addColumn('start_date', 'int');
            $table->addColumn('end_date', 'int')->setDefault(0);
            $table->addColumn('active', 'tinyint', 3)->setDefault(1);
            $table->addColumn('creation_date', 'int');
            $table->addUniqueKey(['item_id', 'code']);
            $table->addKey(['user_id', 'creation_date']);
            $table->addKey(['item_id', 'active', 'start_date', 'end_date']);
        });

/* ... */

        $this->schemaManager()->dropTable('xf_dp_marketplace_coupon');
 
It certainly isn't perfect... but, are you ****ing kidding me right now? 😂

Season 8 Wtf GIF by The Office
 
Scary. It's amazing how fast AI chucks out a load of code as well (it's not always correct, but ..........)
 
Scary. It's amazing how fast AI chucks out a load of code as well (it's not always correct, but ..........)
Right... been using AI for very specific tasks like rewriting jQuery into native code... but this is a whole different level, where it tries to understand internal code/structure for your project... not generic refactoring of code.

Like I didn't even ask it to make a coupon entity or how to define what permissions it should have, or that it even needed a table... but it decided to even make a new table with a schema it thinks would work.
 
So, are you offering coupon discounts in your marketplace addon now?

Make sure to credit the author! 😂
No... but I probably should add that functionality at some point because the whole thing is actually built to handle multiple sellers. So like seller A could offer coupon(s) for their stuff or something.

I still am kind of gobsmacked that I didn't ask it to do anything specific other than create an entity, and it decided the Marketplace addon could probably use coupon functionality. hah
 
What is astounding, and honestly a little concerning, is the rate at which these AI platforms have improved in their code writing abilities in such a short time. In another couple years AI will be writing code by itself to solve problems we didn't even know we had.

Multiple studies out over the last 6 months say AI will replace anywhere from 30-90% of currently known jobs by 2030. That's less than 5 years form now.
 
I’ve been using them a while in Cursor / Windsurf, and Claude Code since it was released in beta a few months back. I’m now using the Claude Max plan, and it’s mind blowing how good it is. I’ve shown @Naz some of the things I’ve been doing with it over the last couple of weeks.
 
What is astounding, and honestly a little concerning, is the rate at which these AI platforms have improved in their code writing abilities in such a short time. In another couple years AI will be writing code by itself to solve problems we didn't even know we had.
Could kind of argue it's doing that now. I mean I didn't ask it to look at my Marketplace addon (or any addon for that matter). I literally just said, "make an entity"... it took it upon itself to look at all the addons installed (as well as XF core code) and from what was there, it decided maybe the Marketplace addon could use a coupon system...

Multiple studies out over the last 6 months say AI will replace anywhere from 30-90% of currently known jobs by 2030. That's less than 5 years form now.
I don't think it's going to replace THAT many jobs... certainly some, but not all. Or maybe they are talking about efficiency. Like if a company employed 100 software engineers, maybe 50 could do the same work with AI tools as far as efficiency?

But really, all jobs tend to get more efficient over time. How many jobs were "replaced" when computers became a thing? 1 accountant could probably do the job of 50 at that point.
 
What is astounding, and honestly a little concerning, is the rate at which these AI platforms have improved in their code writing abilities in such a short time. In another couple years AI will be writing code by itself to solve problems we didn't even know we had.

Multiple studies out over the last 6 months say AI will replace anywhere from 30-90% of currently known jobs by 2030. That's less than 5 years form now.
And that is starting to sound like a futuristic sci movie coming true - AI writing code to empower itself and take over the human race 😂 Did anyone ever see that film "Demon Seed"? The computer actually has a child with the wife 😂
 
Just imagine what AI will capable of when paired with a quantum processor. Prototypes are currently running at 1,000,000,000,000,000 times faster than today's fastest super computer.
 

Attachments

  • 1732213994550.webp
    1732213994550.webp
    151.2 KB · Views: 7
I've been messing around with Junie too and managed to create a thread count add-on with little to no effort. I thought their ordinary AI was amazing until I found Junie. Good lord how far we are coming these days. Completely worth the money in my opinion. Thanks for sharing
 
Using AI every day now. Helped me rewrite a Java app that was crazy old. Its getting too good to ignore how much faster things can get built and deployed.
 
What is astounding, and honestly a little concerning, is the rate at which these AI platforms have improved in their code writing abilities in such a short time. In another couple years AI will be writing code by itself to solve problems we didn't even know we had.
Or creating codes humans cannot stop.
 
What is astounding, and honestly a little concerning, is the rate at which these AI platforms have improved in their code writing abilities in such a short time. In another couple years AI will be writing code by itself to solve problems we didn't even know we had.

Multiple studies out over the last 6 months say AI will replace anywhere from 30-90% of currently known jobs by 2030. That's less than 5 years form now.
Two possibilities exist:
1. People won't need to work anymore.
2. There won't be a need for people.

Then what?
 
Back
Top Bottom