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).

Details of the steps it went through:


... 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:
Finder:
Repository:
Additions to Setup.php file:
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).

Details of the steps it went through:


... 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');